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.

AbstractComponentConnector.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. @VaadinApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.terminal.gwt.client.ui;
  5. import java.util.Set;
  6. import com.google.gwt.user.client.ui.FocusWidget;
  7. import com.google.gwt.user.client.ui.Focusable;
  8. import com.google.gwt.user.client.ui.Widget;
  9. import com.vaadin.terminal.gwt.client.ApplicationConnection;
  10. import com.vaadin.terminal.gwt.client.ComponentConnector;
  11. import com.vaadin.terminal.gwt.client.ComponentContainerConnector;
  12. import com.vaadin.terminal.gwt.client.ComponentState;
  13. import com.vaadin.terminal.gwt.client.ConnectorMap;
  14. import com.vaadin.terminal.gwt.client.LayoutManager;
  15. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  16. import com.vaadin.terminal.gwt.client.TooltipInfo;
  17. import com.vaadin.terminal.gwt.client.UIDL;
  18. import com.vaadin.terminal.gwt.client.Util;
  19. import com.vaadin.terminal.gwt.client.VConsole;
  20. import com.vaadin.terminal.gwt.client.communication.ServerRpc;
  21. import com.vaadin.terminal.gwt.client.communication.SharedState;
  22. import com.vaadin.terminal.gwt.client.communication.StateChangeEvent;
  23. public abstract class AbstractComponentConnector extends AbstractConnector
  24. implements ComponentConnector {
  25. public interface ClickRPC extends ServerRpc {
  26. /**
  27. * Called when a click event has occurred and there are server side
  28. * listeners for the event.
  29. *
  30. * @param mouseDetails
  31. * Details about the mouse when the event took place
  32. */
  33. public void click(MouseEventDetails mouseDetails);
  34. }
  35. private ComponentContainerConnector parent;
  36. private Widget widget;
  37. // shared state from the server to the client
  38. private ComponentState state;
  39. private String lastKnownWidth = "";
  40. private String lastKnownHeight = "";
  41. /**
  42. * Default constructor
  43. */
  44. public AbstractComponentConnector() {
  45. }
  46. /**
  47. * Creates and returns the widget for this VPaintableWidget. This method
  48. * should only be called once when initializing the paintable.
  49. *
  50. * @return
  51. */
  52. protected abstract Widget createWidget();
  53. /**
  54. * Returns the widget associated with this paintable. The widget returned by
  55. * this method must not changed during the life time of the paintable.
  56. *
  57. * @return The widget associated with this paintable
  58. */
  59. public Widget getWidget() {
  60. if (widget == null) {
  61. widget = createWidget();
  62. }
  63. return widget;
  64. }
  65. /**
  66. * Returns the shared state object for this connector.
  67. *
  68. * If overriding this method to return a more specific type, also
  69. * {@link #createState()} must be overridden.
  70. *
  71. * @return current shared state (not null)
  72. */
  73. public ComponentState getState() {
  74. return state;
  75. }
  76. public static boolean isRealUpdate(UIDL uidl) {
  77. return !uidl.hasAttribute("cached");
  78. }
  79. @Override
  80. public void onStateChanged(StateChangeEvent stateChangeEvent) {
  81. super.onStateChanged(stateChangeEvent);
  82. ConnectorMap paintableMap = ConnectorMap.get(getConnection());
  83. if (getState().getDebugId() != null) {
  84. getWidget().getElement().setId(getState().getDebugId());
  85. } else {
  86. getWidget().getElement().setId(null);
  87. }
  88. /*
  89. * Disabled state may affect (override) tabindex so the order must be
  90. * first setting tabindex, then enabled state.
  91. */
  92. if (state instanceof TabIndexState && getWidget() instanceof Focusable) {
  93. ((Focusable) getWidget()).setTabIndex(((TabIndexState) state)
  94. .getTabIndex());
  95. }
  96. if (getWidget() instanceof FocusWidget) {
  97. FocusWidget fw = (FocusWidget) getWidget();
  98. fw.setEnabled(isEnabled());
  99. }
  100. // Style names
  101. String styleName = getStyleNames(getWidget().getStylePrimaryName(),
  102. this);
  103. getWidget().setStyleName(styleName);
  104. // Update tooltip
  105. TooltipInfo tooltipInfo = paintableMap.getTooltipInfo(this, null);
  106. if (getState().hasDescription()) {
  107. tooltipInfo.setTitle(getState().getDescription());
  108. } else {
  109. tooltipInfo.setTitle(null);
  110. }
  111. // add error info to tooltip if present
  112. tooltipInfo.setErrorMessage(getState().getErrorMessage());
  113. // Set captions
  114. if (delegateCaptionHandling()) {
  115. ComponentContainerConnector parent = getParent();
  116. if (parent != null) {
  117. parent.updateCaption(this);
  118. } else if (!(this instanceof RootConnector)) {
  119. VConsole.error("Parent of connector "
  120. + Util.getConnectorString(this)
  121. + " is null. This is typically an indication of a broken component hierarchy");
  122. }
  123. }
  124. /*
  125. * updateComponentSize need to be after caption update so caption can be
  126. * taken into account
  127. */
  128. updateComponentSize();
  129. }
  130. private void updateComponentSize() {
  131. String newWidth = getState().getWidth();
  132. String newHeight = getState().getHeight();
  133. // Parent should be updated if either dimension changed between relative
  134. // and non-relative
  135. if (newWidth.endsWith("%") != lastKnownWidth.endsWith("%")) {
  136. ComponentContainerConnector parent = getParent();
  137. if (parent instanceof ManagedLayout) {
  138. getLayoutManager().setWidthNeedsUpdate((ManagedLayout) parent);
  139. }
  140. }
  141. if (newHeight.endsWith("%") != lastKnownHeight.endsWith("%")) {
  142. ComponentContainerConnector parent = getParent();
  143. if (parent instanceof ManagedLayout) {
  144. getLayoutManager().setHeightNeedsUpdate((ManagedLayout) parent);
  145. }
  146. }
  147. lastKnownWidth = newWidth;
  148. lastKnownHeight = newHeight;
  149. // Set defined sizes
  150. Widget widget = getWidget();
  151. widget.setStyleName("v-has-width", !isUndefinedWidth());
  152. widget.setStyleName("v-has-height", !isUndefinedHeight());
  153. widget.setHeight(newHeight);
  154. widget.setWidth(newWidth);
  155. }
  156. public boolean isRelativeHeight() {
  157. return getState().getHeight().endsWith("%");
  158. }
  159. public boolean isRelativeWidth() {
  160. return getState().getWidth().endsWith("%");
  161. }
  162. public boolean isUndefinedHeight() {
  163. return getState().getHeight().length() == 0;
  164. }
  165. public boolean isUndefinedWidth() {
  166. return getState().getWidth().length() == 0;
  167. }
  168. /*
  169. * (non-Javadoc)
  170. *
  171. * @see com.vaadin.terminal.gwt.client.Connector#isEnabled()
  172. */
  173. public boolean isEnabled() {
  174. if (!getState().isEnabled()) {
  175. return false;
  176. }
  177. if (getParent() == null) {
  178. return true;
  179. } else {
  180. return getParent().isEnabled();
  181. }
  182. }
  183. /**
  184. * Return true if parent handles caption, false if the paintable handles the
  185. * caption itself.
  186. *
  187. *
  188. * @deprecated This should always return true and all components should let
  189. * the parent handle the caption and use other attributes for
  190. * internal texts in the component
  191. * @return
  192. */
  193. @Deprecated
  194. protected boolean delegateCaptionHandling() {
  195. return true;
  196. }
  197. /**
  198. * Generates the style name for the widget based on the given primary style
  199. * name (typically returned by Widget.getPrimaryStyleName()) and the UIDL
  200. * and shared state of the component. An additional "modified" style name
  201. * can be added if the field parameter is set to true.
  202. *
  203. * @param primaryStyleName
  204. * @param uidl
  205. * @param state
  206. * component shared state
  207. * @param field
  208. * @return
  209. */
  210. protected static String getStyleNames(String primaryStyleName,
  211. ComponentConnector connector) {
  212. ComponentState state = connector.getState();
  213. StringBuffer styleBuf = new StringBuffer();
  214. styleBuf.append(primaryStyleName);
  215. styleBuf.append(" v-paintable");
  216. // Uses connector methods to enable connectors to take hierarchy or
  217. // multiple state variables into account
  218. if (!connector.isEnabled()) {
  219. styleBuf.append(" ");
  220. styleBuf.append(ApplicationConnection.DISABLED_CLASSNAME);
  221. }
  222. if (connector.isReadOnly()) {
  223. styleBuf.append(" ");
  224. styleBuf.append("v-readonly");
  225. }
  226. // add additional styles as css classes, prefixed with component default
  227. // stylename
  228. if (state.hasStyles()) {
  229. for (String style : state.getStyles()) {
  230. styleBuf.append(" ");
  231. styleBuf.append(primaryStyleName);
  232. styleBuf.append("-");
  233. styleBuf.append(style);
  234. styleBuf.append(" ");
  235. styleBuf.append(style);
  236. }
  237. }
  238. if (connector instanceof AbstractFieldConnector) {
  239. // TODO Move to AbstractFieldConnector
  240. AbstractFieldConnector afc = ((AbstractFieldConnector) connector);
  241. if (afc.isModified()) {
  242. // add modified classname to Fields
  243. styleBuf.append(" ");
  244. styleBuf.append(ApplicationConnection.MODIFIED_CLASSNAME);
  245. }
  246. if (afc.isRequired()) {
  247. // add required classname to required fields
  248. styleBuf.append(" ");
  249. styleBuf.append(primaryStyleName);
  250. styleBuf.append(ApplicationConnection.REQUIRED_CLASSNAME_EXT);
  251. }
  252. }
  253. // add error classname to components w/ error
  254. if (null != state.getErrorMessage()) {
  255. styleBuf.append(" ");
  256. styleBuf.append(primaryStyleName);
  257. styleBuf.append(ApplicationConnection.ERROR_CLASSNAME_EXT);
  258. }
  259. return styleBuf.toString();
  260. }
  261. /*
  262. * (non-Javadoc)
  263. *
  264. * @see com.vaadin.terminal.gwt.client.ComponentConnector#isReadOnly()
  265. */
  266. @Deprecated
  267. public boolean isReadOnly() {
  268. return getState().isReadOnly();
  269. }
  270. /**
  271. * Sets the shared state for the paintable widget.
  272. *
  273. * @param new shared state (must be compatible with the return value of
  274. * {@link #getState()} - {@link ComponentState} if
  275. * {@link #getState()} is not overridden
  276. */
  277. public final void setState(SharedState state) {
  278. this.state = (ComponentState) state;
  279. }
  280. public LayoutManager getLayoutManager() {
  281. return LayoutManager.get(getConnection());
  282. }
  283. /*
  284. * (non-Javadoc)
  285. *
  286. * @see com.vaadin.terminal.gwt.client.Connector#getParent()
  287. */
  288. public ComponentContainerConnector getParent() {
  289. return parent;
  290. }
  291. /*
  292. * (non-Javadoc)
  293. *
  294. * @see
  295. * com.vaadin.terminal.gwt.client.Connector#setParent(com.vaadin.terminal
  296. * .gwt.client.ComponentContainerConnector)
  297. */
  298. public void setParent(ComponentContainerConnector parent) {
  299. this.parent = parent;
  300. }
  301. /**
  302. * Checks if there is a registered server side listener for the given event
  303. * identifier.
  304. *
  305. * @param eventIdentifier
  306. * The identifier to check for
  307. * @return true if an event listener has been registered with the given
  308. * event identifier on the server side, false otherwise
  309. */
  310. public boolean hasEventListener(String eventIdentifier) {
  311. Set<String> reg = getState().getRegisteredEventListeners();
  312. return (reg != null && reg.contains(eventIdentifier));
  313. }
  314. @Override
  315. public void onUnregister() {
  316. super.onUnregister();
  317. // Warn if widget is still attached to DOM. It should never be at this
  318. // point.
  319. if (getWidget() != null && getWidget().isAttached()) {
  320. VConsole.log("Widget for unregistered connector "
  321. + Util.getConnectorString(this)
  322. + " is still attached to the DOM.");
  323. }
  324. }
  325. }