PageRenderTime 35ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/server/src/com/vaadin/ui/AbstractSplitPanel.java

https://gitlab.com/jforge/vaadin
Java | 766 lines | 400 code | 79 blank | 287 comment | 89 complexity | 3113d056abd873d71d4bc06252ba0da1 MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0-no-copyleft-exception, 0BSD, EPL-1.0, LGPL-2.0, LGPL-2.1, BSD-3-Clause, JSON
  1. /*
  2. * Copyright 2000-2014 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.Collection;
  20. import java.util.Iterator;
  21. import org.jsoup.nodes.Element;
  22. import com.vaadin.event.ConnectorEventListener;
  23. import com.vaadin.event.MouseEvents.ClickEvent;
  24. import com.vaadin.server.SizeWithUnit;
  25. import com.vaadin.server.Sizeable;
  26. import com.vaadin.shared.EventId;
  27. import com.vaadin.shared.MouseEventDetails;
  28. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelRpc;
  29. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState;
  30. import com.vaadin.shared.ui.splitpanel.AbstractSplitPanelState.SplitterState;
  31. import com.vaadin.ui.declarative.DesignAttributeHandler;
  32. import com.vaadin.ui.declarative.DesignContext;
  33. import com.vaadin.ui.declarative.DesignException;
  34. import com.vaadin.util.ReflectTools;
  35. /**
  36. * AbstractSplitPanel.
  37. *
  38. * <code>AbstractSplitPanel</code> is base class for a component container that
  39. * can contain two components. The components are split by a divider element.
  40. *
  41. * @author Vaadin Ltd.
  42. * @since 6.5
  43. */
  44. public abstract class AbstractSplitPanel extends AbstractComponentContainer {
  45. // TODO use Unit in AbstractSplitPanelState and remove these
  46. private Unit posUnit;
  47. private Unit posMinUnit;
  48. private Unit posMaxUnit;
  49. private AbstractSplitPanelRpc rpc = new AbstractSplitPanelRpc() {
  50. @Override
  51. public void splitterClick(MouseEventDetails mouseDetails) {
  52. fireEvent(new SplitterClickEvent(AbstractSplitPanel.this,
  53. mouseDetails));
  54. }
  55. @Override
  56. public void setSplitterPosition(float position) {
  57. getSplitterState().position = position;
  58. fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this,
  59. position, getSplitPositionUnit()));
  60. }
  61. };
  62. public AbstractSplitPanel() {
  63. registerRpc(rpc);
  64. setSplitPosition(50, Unit.PERCENTAGE, false);
  65. setSplitPositionLimits(0, Unit.PERCENTAGE, 100, Unit.PERCENTAGE);
  66. }
  67. /**
  68. * Modifiable and Serializable Iterator for the components, used by
  69. * {@link AbstractSplitPanel#getComponentIterator()}.
  70. */
  71. private class ComponentIterator implements Iterator<Component>,
  72. Serializable {
  73. int i = 0;
  74. @Override
  75. public boolean hasNext() {
  76. if (i < getComponentCount()) {
  77. return true;
  78. }
  79. return false;
  80. }
  81. @Override
  82. public Component next() {
  83. if (!hasNext()) {
  84. return null;
  85. }
  86. i++;
  87. if (i == 1) {
  88. return (getFirstComponent() == null ? getSecondComponent()
  89. : getFirstComponent());
  90. } else if (i == 2) {
  91. return getSecondComponent();
  92. }
  93. return null;
  94. }
  95. @Override
  96. public void remove() {
  97. if (i == 1) {
  98. if (getFirstComponent() != null) {
  99. setFirstComponent(null);
  100. i = 0;
  101. } else {
  102. setSecondComponent(null);
  103. }
  104. } else if (i == 2) {
  105. setSecondComponent(null);
  106. }
  107. }
  108. }
  109. /**
  110. * Add a component into this container. The component is added to the right
  111. * or under the previous component.
  112. *
  113. * @param c
  114. * the component to be added.
  115. */
  116. @Override
  117. public void addComponent(Component c) {
  118. if (getFirstComponent() == null) {
  119. setFirstComponent(c);
  120. } else if (getSecondComponent() == null) {
  121. setSecondComponent(c);
  122. } else {
  123. throw new UnsupportedOperationException(
  124. "Split panel can contain only two components");
  125. }
  126. }
  127. /**
  128. * Sets the first component of this split panel. Depending on the direction
  129. * the first component is shown at the top or to the left.
  130. *
  131. * @param c
  132. * The component to use as first component
  133. */
  134. public void setFirstComponent(Component c) {
  135. if (getFirstComponent() == c) {
  136. // Nothing to do
  137. return;
  138. }
  139. if (getFirstComponent() != null) {
  140. // detach old
  141. removeComponent(getFirstComponent());
  142. }
  143. getState().firstChild = c;
  144. if (c != null) {
  145. super.addComponent(c);
  146. }
  147. }
  148. /**
  149. * Sets the second component of this split panel. Depending on the direction
  150. * the second component is shown at the bottom or to the left.
  151. *
  152. * @param c
  153. * The component to use as first component
  154. */
  155. public void setSecondComponent(Component c) {
  156. if (getSecondComponent() == c) {
  157. // Nothing to do
  158. return;
  159. }
  160. if (getSecondComponent() != null) {
  161. // detach old
  162. removeComponent(getSecondComponent());
  163. }
  164. getState().secondChild = c;
  165. if (c != null) {
  166. super.addComponent(c);
  167. }
  168. }
  169. /**
  170. * Gets the first component of this split panel. Depending on the direction
  171. * this is either the component shown at the top or to the left.
  172. *
  173. * @return the first component of this split panel
  174. */
  175. public Component getFirstComponent() {
  176. return (Component) getState(false).firstChild;
  177. }
  178. /**
  179. * Gets the second component of this split panel. Depending on the direction
  180. * this is either the component shown at the top or to the left.
  181. *
  182. * @return the second component of this split panel
  183. */
  184. public Component getSecondComponent() {
  185. return (Component) getState(false).secondChild;
  186. }
  187. /**
  188. * Removes the component from this container.
  189. *
  190. * @param c
  191. * the component to be removed.
  192. */
  193. @Override
  194. public void removeComponent(Component c) {
  195. super.removeComponent(c);
  196. if (c == getFirstComponent()) {
  197. getState().firstChild = null;
  198. } else if (c == getSecondComponent()) {
  199. getState().secondChild = null;
  200. }
  201. }
  202. /*
  203. * (non-Javadoc)
  204. *
  205. * @see com.vaadin.ui.ComponentContainer#getComponentIterator()
  206. */
  207. @Override
  208. public Iterator<Component> iterator() {
  209. return new ComponentIterator();
  210. }
  211. /**
  212. * Gets the number of contained components. Consistent with the iterator
  213. * returned by {@link #getComponentIterator()}.
  214. *
  215. * @return the number of contained components (zero, one or two)
  216. */
  217. @Override
  218. public int getComponentCount() {
  219. int count = 0;
  220. if (getFirstComponent() != null) {
  221. count++;
  222. }
  223. if (getSecondComponent() != null) {
  224. count++;
  225. }
  226. return count;
  227. }
  228. /* Documented in superclass */
  229. @Override
  230. public void replaceComponent(Component oldComponent, Component newComponent) {
  231. if (oldComponent == getFirstComponent()) {
  232. setFirstComponent(newComponent);
  233. } else if (oldComponent == getSecondComponent()) {
  234. setSecondComponent(newComponent);
  235. }
  236. }
  237. /**
  238. * Moves the position of the splitter.
  239. *
  240. * @param pos
  241. * the new size of the first region in the unit that was last
  242. * used (default is percentage). Fractions are only allowed when
  243. * unit is percentage.
  244. */
  245. public void setSplitPosition(float pos) {
  246. setSplitPosition(pos, posUnit, false);
  247. }
  248. /**
  249. * Moves the position of the splitter.
  250. *
  251. * @param pos
  252. * the new size of the region in the unit that was last used
  253. * (default is percentage). Fractions are only allowed when unit
  254. * is percentage.
  255. *
  256. * @param reverse
  257. * if set to true the split splitter position is measured by the
  258. * second region else it is measured by the first region
  259. */
  260. public void setSplitPosition(float pos, boolean reverse) {
  261. setSplitPosition(pos, posUnit, reverse);
  262. }
  263. /**
  264. * Moves the position of the splitter with given position and unit.
  265. *
  266. * @param pos
  267. * the new size of the first region. Fractions are only allowed
  268. * when unit is percentage.
  269. * @param unit
  270. * the unit (from {@link Sizeable}) in which the size is given.
  271. */
  272. public void setSplitPosition(float pos, Unit unit) {
  273. setSplitPosition(pos, unit, false);
  274. }
  275. /**
  276. * Moves the position of the splitter with given position and unit.
  277. *
  278. * @param pos
  279. * the new size of the first region. Fractions are only allowed
  280. * when unit is percentage.
  281. * @param unit
  282. * the unit (from {@link Sizeable}) in which the size is given.
  283. * @param reverse
  284. * if set to true the split splitter position is measured by the
  285. * second region else it is measured by the first region
  286. *
  287. */
  288. public void setSplitPosition(float pos, Unit unit, boolean reverse) {
  289. if (unit != Unit.PERCENTAGE && unit != Unit.PIXELS) {
  290. throw new IllegalArgumentException(
  291. "Only percentage and pixel units are allowed");
  292. }
  293. if (unit != Unit.PERCENTAGE) {
  294. pos = Math.round(pos);
  295. }
  296. SplitterState splitterState = getSplitterState();
  297. splitterState.position = pos;
  298. splitterState.positionUnit = unit.getSymbol();
  299. splitterState.positionReversed = reverse;
  300. posUnit = unit;
  301. fireEvent(new SplitPositionChangeEvent(AbstractSplitPanel.this, pos,
  302. posUnit));
  303. }
  304. /**
  305. * Returns the current position of the splitter, in
  306. * {@link #getSplitPositionUnit()} units.
  307. *
  308. * @return position of the splitter
  309. */
  310. public float getSplitPosition() {
  311. return getSplitterState(false).position;
  312. }
  313. /**
  314. * Returns the unit of position of the splitter
  315. *
  316. * @return unit of position of the splitter
  317. * @see #setSplitPosition(float, Unit)
  318. */
  319. public Unit getSplitPositionUnit() {
  320. return posUnit;
  321. }
  322. /**
  323. * Is the split position reversed. By default the split position is measured
  324. * by the first region, but if split position is reversed the measuring is
  325. * done by the second region instead.
  326. *
  327. * @since 7.3.6
  328. * @return {@code true} if reversed, {@code false} otherwise.
  329. * @see #setSplitPosition(float, boolean)
  330. */
  331. public boolean isSplitPositionReversed() {
  332. return getSplitterState(false).positionReversed;
  333. }
  334. /**
  335. * Sets the minimum split position to the given position and unit. If the
  336. * split position is reversed, maximum and minimum are also reversed.
  337. *
  338. * @param pos
  339. * the minimum position of the split
  340. * @param unit
  341. * the unit (from {@link Sizeable}) in which the size is given.
  342. * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
  343. */
  344. public void setMinSplitPosition(float pos, Unit unit) {
  345. setSplitPositionLimits(pos, unit, getSplitterState(false).maxPosition,
  346. posMaxUnit);
  347. }
  348. /**
  349. * Returns the current minimum position of the splitter, in
  350. * {@link #getMinSplitPositionUnit()} units.
  351. *
  352. * @return the minimum position of the splitter
  353. */
  354. public float getMinSplitPosition() {
  355. return getSplitterState(false).minPosition;
  356. }
  357. /**
  358. * Returns the unit of the minimum position of the splitter.
  359. *
  360. * @return the unit of the minimum position of the splitter
  361. */
  362. public Unit getMinSplitPositionUnit() {
  363. return posMinUnit;
  364. }
  365. /**
  366. * Sets the maximum split position to the given position and unit. If the
  367. * split position is reversed, maximum and minimum are also reversed.
  368. *
  369. * @param pos
  370. * the maximum position of the split
  371. * @param unit
  372. * the unit (from {@link Sizeable}) in which the size is given.
  373. * Allowed units are UNITS_PERCENTAGE and UNITS_PIXELS
  374. */
  375. public void setMaxSplitPosition(float pos, Unit unit) {
  376. setSplitPositionLimits(getSplitterState(false).minPosition, posMinUnit,
  377. pos, unit);
  378. }
  379. /**
  380. * Returns the current maximum position of the splitter, in
  381. * {@link #getMaxSplitPositionUnit()} units.
  382. *
  383. * @return the maximum position of the splitter
  384. */
  385. public float getMaxSplitPosition() {
  386. return getSplitterState(false).maxPosition;
  387. }
  388. /**
  389. * Returns the unit of the maximum position of the splitter
  390. *
  391. * @return the unit of the maximum position of the splitter
  392. */
  393. public Unit getMaxSplitPositionUnit() {
  394. return posMaxUnit;
  395. }
  396. /**
  397. * Sets the maximum and minimum position of the splitter. If the split
  398. * position is reversed, maximum and minimum are also reversed.
  399. *
  400. * @param minPos
  401. * the new minimum position
  402. * @param minPosUnit
  403. * the unit (from {@link Sizeable}) in which the minimum position
  404. * is given.
  405. * @param maxPos
  406. * the new maximum position
  407. * @param maxPosUnit
  408. * the unit (from {@link Sizeable}) in which the maximum position
  409. * is given.
  410. */
  411. private void setSplitPositionLimits(float minPos, Unit minPosUnit,
  412. float maxPos, Unit maxPosUnit) {
  413. if ((minPosUnit != Unit.PERCENTAGE && minPosUnit != Unit.PIXELS)
  414. || (maxPosUnit != Unit.PERCENTAGE && maxPosUnit != Unit.PIXELS)) {
  415. throw new IllegalArgumentException(
  416. "Only percentage and pixel units are allowed");
  417. }
  418. SplitterState state = getSplitterState();
  419. state.minPosition = minPos;
  420. state.minPositionUnit = minPosUnit.getSymbol();
  421. posMinUnit = minPosUnit;
  422. state.maxPosition = maxPos;
  423. state.maxPositionUnit = maxPosUnit.getSymbol();
  424. posMaxUnit = maxPosUnit;
  425. }
  426. /**
  427. * Lock the SplitPanels position, disabling the user from dragging the split
  428. * handle.
  429. *
  430. * @param locked
  431. * Set <code>true</code> if locked, <code>false</code> otherwise.
  432. */
  433. public void setLocked(boolean locked) {
  434. getSplitterState().locked = locked;
  435. }
  436. /**
  437. * Is the SplitPanel handle locked (user not allowed to change split
  438. * position by dragging).
  439. *
  440. * @return <code>true</code> if locked, <code>false</code> otherwise.
  441. */
  442. public boolean isLocked() {
  443. return getSplitterState(false).locked;
  444. }
  445. /**
  446. * <code>SplitterClickListener</code> interface for listening for
  447. * <code>SplitterClickEvent</code> fired by a <code>SplitPanel</code>.
  448. *
  449. * @see SplitterClickEvent
  450. * @since 6.2
  451. */
  452. public interface SplitterClickListener extends ConnectorEventListener {
  453. public static final Method clickMethod = ReflectTools.findMethod(
  454. SplitterClickListener.class, "splitterClick",
  455. SplitterClickEvent.class);
  456. /**
  457. * SplitPanel splitter has been clicked
  458. *
  459. * @param event
  460. * SplitterClickEvent event.
  461. */
  462. public void splitterClick(SplitterClickEvent event);
  463. }
  464. public static class SplitterClickEvent extends ClickEvent {
  465. public SplitterClickEvent(Component source,
  466. MouseEventDetails mouseEventDetails) {
  467. super(source, mouseEventDetails);
  468. }
  469. }
  470. /**
  471. * Interface for listening for {@link SplitPositionChangeEvent}s fired by a
  472. * SplitPanel.
  473. *
  474. * @since 7.5.0
  475. */
  476. public interface SplitPositionChangeListener extends ConnectorEventListener {
  477. public static final Method moveMethod = ReflectTools.findMethod(
  478. SplitPositionChangeListener.class, "onSplitPositionChanged",
  479. SplitPositionChangeEvent.class);
  480. /**
  481. * SplitPanel splitter position has been changed.
  482. *
  483. * @param event
  484. * SplitPositionChangeEvent event.
  485. */
  486. public void onSplitPositionChanged(SplitPositionChangeEvent event);
  487. }
  488. /**
  489. * Event that indicates a change in SplitPanel's splitter position.
  490. *
  491. * @since 7.5.0
  492. */
  493. public static class SplitPositionChangeEvent extends Component.Event {
  494. private final float position;
  495. private final Unit unit;
  496. public SplitPositionChangeEvent(final Component source,
  497. final float position, final Unit unit) {
  498. super(source);
  499. this.position = position;
  500. this.unit = unit;
  501. }
  502. public float getSplitPosition() {
  503. return position;
  504. }
  505. public Unit getSplitPositionUnit() {
  506. return unit;
  507. }
  508. }
  509. public void addSplitterClickListener(SplitterClickListener listener) {
  510. addListener(EventId.CLICK_EVENT_IDENTIFIER, SplitterClickEvent.class,
  511. listener, SplitterClickListener.clickMethod);
  512. }
  513. /**
  514. * @deprecated As of 7.0, replaced by
  515. * {@link #addSplitterClickListener(SplitterClickListener)}
  516. **/
  517. @Deprecated
  518. public void addListener(SplitterClickListener listener) {
  519. addSplitterClickListener(listener);
  520. }
  521. public void removeSplitterClickListener(SplitterClickListener listener) {
  522. removeListener(EventId.CLICK_EVENT_IDENTIFIER,
  523. SplitterClickEvent.class, listener);
  524. }
  525. /**
  526. * @deprecated As of 7.0, replaced by
  527. * {@link #removeSplitterClickListener(SplitterClickListener)}
  528. **/
  529. @Deprecated
  530. public void removeListener(SplitterClickListener listener) {
  531. removeSplitterClickListener(listener);
  532. }
  533. /**
  534. * Register a listener to handle {@link SplitPositionChangeEvent}s.
  535. *
  536. * @since 7.5.0
  537. * @param listener
  538. * {@link SplitPositionChangeListener} to be registered.
  539. */
  540. public void addSplitPositionChangeListener(
  541. SplitPositionChangeListener listener) {
  542. addListener(SplitPositionChangeEvent.class, listener,
  543. SplitPositionChangeListener.moveMethod);
  544. }
  545. /**
  546. * Removes a {@link SplitPositionChangeListener}.
  547. *
  548. * @since 7.5.0
  549. * @param listener
  550. * SplitPositionChangeListener to be removed.
  551. */
  552. public void removeSplitPositionChangeListener(
  553. SplitPositionChangeListener listener) {
  554. removeListener(SplitPositionChangeEvent.class, listener);
  555. }
  556. @Override
  557. protected AbstractSplitPanelState getState() {
  558. return (AbstractSplitPanelState) super.getState();
  559. }
  560. @Override
  561. protected AbstractSplitPanelState getState(boolean markAsDirty) {
  562. return (AbstractSplitPanelState) super.getState(markAsDirty);
  563. }
  564. private SplitterState getSplitterState() {
  565. return ((AbstractSplitPanelState) super.getState()).splitterState;
  566. }
  567. private SplitterState getSplitterState(boolean markAsDirty) {
  568. return ((AbstractSplitPanelState) super.getState(markAsDirty)).splitterState;
  569. }
  570. /*
  571. * (non-Javadoc)
  572. *
  573. * @see com.vaadin.ui.AbstractComponent#readDesign(org.jsoup.nodes .Element,
  574. * com.vaadin.ui.declarative.DesignContext)
  575. */
  576. @Override
  577. public void readDesign(Element design, DesignContext designContext) {
  578. // handle default attributes
  579. super.readDesign(design, designContext);
  580. // handle custom attributes, use default values if no explicit value
  581. // set
  582. // There is no setter for reversed, so it will be handled using
  583. // setSplitPosition.
  584. boolean reversed = false;
  585. if (design.hasAttr("reversed")) {
  586. reversed = DesignAttributeHandler.readAttribute("reversed",
  587. design.attributes(), Boolean.class);
  588. setSplitPosition(getSplitPosition(), reversed);
  589. }
  590. if (design.hasAttr("split-position")) {
  591. SizeWithUnit splitPosition = SizeWithUnit.parseStringSize(
  592. design.attr("split-position"), Unit.PERCENTAGE);
  593. setSplitPosition(splitPosition.getSize(), splitPosition.getUnit(),
  594. reversed);
  595. }
  596. if (design.hasAttr("min-split-position")) {
  597. SizeWithUnit minSplitPosition = SizeWithUnit.parseStringSize(
  598. design.attr("min-split-position"), Unit.PERCENTAGE);
  599. setMinSplitPosition(minSplitPosition.getSize(),
  600. minSplitPosition.getUnit());
  601. }
  602. if (design.hasAttr("max-split-position")) {
  603. SizeWithUnit maxSplitPosition = SizeWithUnit.parseStringSize(
  604. design.attr("max-split-position"), Unit.PERCENTAGE);
  605. setMaxSplitPosition(maxSplitPosition.getSize(),
  606. maxSplitPosition.getUnit());
  607. }
  608. // handle children
  609. if (design.children().size() > 2) {
  610. throw new DesignException(
  611. "A split panel can contain at most two components.");
  612. }
  613. for (Element childElement : design.children()) {
  614. Component childComponent = designContext.readDesign(childElement);
  615. if (childElement.hasAttr(":second")) {
  616. setSecondComponent(childComponent);
  617. } else {
  618. addComponent(childComponent);
  619. }
  620. }
  621. }
  622. @Override
  623. protected Collection<String> getCustomAttributes() {
  624. Collection<String> attributes = super.getCustomAttributes();
  625. // the setters of the properties do not accept strings such as "20px"
  626. attributes.add("split-position");
  627. attributes.add("min-split-position");
  628. attributes.add("max-split-position");
  629. // no explicit setter for reversed
  630. attributes.add("reversed");
  631. return attributes;
  632. }
  633. @Override
  634. public void writeDesign(Element design, DesignContext designContext) {
  635. // handle default attributes (also clears children and attributes)
  636. super.writeDesign(design, designContext);
  637. // handle custom attributes (write only if a value is not the
  638. // default value)
  639. AbstractSplitPanel def = (AbstractSplitPanel) designContext
  640. .getDefaultInstance(this);
  641. if (getSplitPosition() != def.getSplitPosition()
  642. || !def.getSplitPositionUnit().equals(getSplitPositionUnit())) {
  643. String splitPositionString = asString(getSplitPosition())
  644. + getSplitPositionUnit();
  645. design.attr("split-position", splitPositionString);
  646. }
  647. if (getMinSplitPosition() != def.getMinSplitPosition()
  648. || !def.getMinSplitPositionUnit().equals(
  649. getMinSplitPositionUnit())) {
  650. design.attr("min-split-position", asString(getMinSplitPosition())
  651. + getMinSplitPositionUnit());
  652. }
  653. if (getMaxSplitPosition() != def.getMaxSplitPosition()
  654. || !def.getMaxSplitPositionUnit().equals(
  655. getMaxSplitPositionUnit())) {
  656. design.attr("max-split-position", asString(getMaxSplitPosition())
  657. + getMaxSplitPositionUnit());
  658. }
  659. if (getSplitterState().positionReversed) {
  660. design.attr("reversed", "");
  661. }
  662. // handle child components
  663. if (!designContext.shouldWriteChildren(this, def)) {
  664. return;
  665. }
  666. Component firstComponent = getFirstComponent();
  667. Component secondComponent = getSecondComponent();
  668. if (firstComponent != null) {
  669. Element childElement = designContext.createElement(firstComponent);
  670. design.appendChild(childElement);
  671. }
  672. if (secondComponent != null) {
  673. Element childElement = designContext.createElement(secondComponent);
  674. if (firstComponent == null) {
  675. childElement.attr(":second", "");
  676. }
  677. design.appendChild(childElement);
  678. }
  679. }
  680. private String asString(float number) {
  681. int truncated = (int) number;
  682. if (truncated == number) {
  683. return "" + truncated;
  684. }
  685. return "" + number;
  686. }
  687. }