PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/qooxdoo/framework/source/class/qx/data/controller/Object.js

https://github.com/stephaneerard/qooxdoo
JavaScript | 359 lines | 148 code | 36 blank | 175 comment | 41 complexity | 48b46b3b41a8788143e491987f6c25bb MD5 | raw file
  1. /* ************************************************************************
  2. qooxdoo - the new era of web development
  3. http://qooxdoo.org
  4. Copyright:
  5. 2004-2009 1&1 Internet AG, Germany, http://www.1und1.de
  6. License:
  7. LGPL: http://www.gnu.org/licenses/lgpl.html
  8. EPL: http://www.eclipse.org/org/documents/epl-v10.php
  9. See the LICENSE file in the project's top-level directory for details.
  10. Authors:
  11. * Martin Wittemann (martinwittemann)
  12. ************************************************************************ */
  13. /**
  14. * <h2>Object Controller</h2>
  15. *
  16. * *General idea*
  17. *
  18. * The idea of the object controller is to make the binding of one model object
  19. * containing one or more properties as easy as possible. Therefore the
  20. * controller can take a model as property. Every property in that model can be
  21. * bound to one ore more targets properties. The binding will be for
  22. * atomic types only like Numbers, Strings, ...
  23. *
  24. * *Features*
  25. *
  26. * * Manages the bindings between the model properties and the different targets
  27. * * No need for the user to take care of the binding ids
  28. * * Can create an bidirectional binding (read- / write-binding)
  29. * * Handles the change of the model which means adding the old targets
  30. *
  31. * *Usage*
  32. *
  33. * The controller only can work if a model is set. If the model property is
  34. * null, the controller is not working. But it can be null on any time.
  35. *
  36. * *Cross reference*
  37. *
  38. * * If you want to bind a list like widget, use {@link qx.data.controller.List}
  39. * * If you want to bind a tree widget, use {@link qx.data.controller.Tree}
  40. * * If you want to bind a form widget, use {@link qx.data.controller.Form}
  41. */
  42. qx.Class.define("qx.data.controller.Object",
  43. {
  44. extend : qx.core.Object,
  45. /*
  46. *****************************************************************************
  47. CONSTRUCTOR
  48. *****************************************************************************
  49. */
  50. /**
  51. * @param model {qx.core.Object?null} The model for the model property.
  52. */
  53. construct : function(model)
  54. {
  55. this.base(arguments);
  56. // create a map for all created binding ids
  57. this.__bindings = {};
  58. // create an array to store all current targets
  59. this.__targets = [];
  60. if (model != null) {
  61. this.setModel(model);
  62. }
  63. },
  64. /*
  65. *****************************************************************************
  66. PROPERTIES
  67. *****************************************************************************
  68. */
  69. properties :
  70. {
  71. /** The model object which does have the properties for the binding. */
  72. model :
  73. {
  74. check: "qx.core.Object",
  75. event: "changeModel",
  76. apply: "_applyModel",
  77. nullable: true
  78. }
  79. },
  80. /*
  81. *****************************************************************************
  82. MEMBERS
  83. *****************************************************************************
  84. */
  85. members :
  86. {
  87. // private members
  88. __targets : null,
  89. __bindings : null,
  90. /**
  91. * Apply-method which will be called if a new model has been set.
  92. * All bindings will be moved to the new model.
  93. *
  94. * @param value {qx.core.Object|null} The new model.
  95. * @param old {qx.core.Object|null} The old model.
  96. */
  97. _applyModel: function(value, old) {
  98. // for every target
  99. for (var i = 0; i < this.__targets.length; i++) {
  100. // get the properties
  101. var targetObject = this.__targets[i][0];
  102. var targetProperty = this.__targets[i][1];
  103. var sourceProperty = this.__targets[i][2];
  104. var bidirectional = this.__targets[i][3];
  105. var options = this.__targets[i][4];
  106. var reverseOptions = this.__targets[i][5];
  107. // remove it from the old if possible
  108. if (old != undefined) {
  109. this.__removeTargetFrom(targetObject, targetProperty, sourceProperty, old);
  110. }
  111. // add it to the new if available
  112. if (value != undefined) {
  113. this.__addTarget(
  114. targetObject, targetProperty, sourceProperty, bidirectional,
  115. options, reverseOptions
  116. );
  117. } else {
  118. // in shutdown situations, it may be that the target is already disposed
  119. if (targetObject.isDisposed()) {
  120. continue;
  121. }
  122. // if the model is null, reset the current target
  123. if (targetProperty.indexOf("[") == -1) {
  124. targetObject["reset" + qx.lang.String.firstUp(targetProperty)]();
  125. } else {
  126. var open = targetProperty.indexOf("[");
  127. var index = parseInt(
  128. targetProperty.substring(open + 1, targetProperty.length - 1)
  129. );
  130. targetProperty = targetProperty.substring(0, open);
  131. var targetArray = targetObject["get" + qx.lang.String.firstUp(targetProperty)]();
  132. if (index == "last") {
  133. index = targetArray.length;
  134. }
  135. if (targetArray) {
  136. targetArray.setItem(index, null);
  137. }
  138. }
  139. }
  140. }
  141. },
  142. /**
  143. * Adds a new target to the controller. After adding the target, the given
  144. * property of the model will be bound to the targets property.
  145. *
  146. * @param targetObject {qx.core.Object} The object on which the property
  147. * should be bound.
  148. *
  149. * @param targetProperty {String} The property to which the binding should
  150. * go.
  151. *
  152. * @param sourceProperty {String} The name of the property in the model.
  153. *
  154. * @param bidirectional {Boolean?false} Signals if the binding should also work
  155. * in the reverse direction, from the target to source.
  156. *
  157. * @param options {Map?null} The options Map used by the binding from source
  158. * to target. The possible options can be found in the
  159. * {@link qx.data.SingleValueBinding} class.
  160. *
  161. * @param reverseOptions {Map?null} The options used by the binding in the
  162. * reverse direction. The possible options can be found in the
  163. * {@link qx.data.SingleValueBinding} class.
  164. */
  165. addTarget: function(
  166. targetObject, targetProperty, sourceProperty,
  167. bidirectional, options, reverseOptions
  168. ) {
  169. // store the added target
  170. this.__targets.push([
  171. targetObject, targetProperty, sourceProperty,
  172. bidirectional, options, reverseOptions
  173. ]);
  174. // delegate the adding
  175. this.__addTarget(
  176. targetObject, targetProperty, sourceProperty,
  177. bidirectional, options, reverseOptions
  178. );
  179. },
  180. /**
  181. * Does the work for {@link #addTarget} but without saving the target
  182. * to the internal target registry.
  183. *
  184. * @param targetObject {qx.core.Object} The object on which the property
  185. * should be bound.
  186. *
  187. * @param targetProperty {String} The property to which the binding should
  188. * go.
  189. *
  190. * @param sourceProperty {String} The name of the property in the model.
  191. *
  192. * @param bidirectional {Boolean?false} Signals if the binding should also work
  193. * in the reverse direction, from the target to source.
  194. *
  195. * @param options {Map?null} The options Map used by the binding from source
  196. * to target. The possible options can be found in the
  197. * {@link qx.data.SingleValueBinding} class.
  198. *
  199. * @param reverseOptions {Map?null} The options used by the binding in the
  200. * reverse direction. The possible options can be found in the
  201. * {@link qx.data.SingleValueBinding} class.
  202. */
  203. __addTarget: function(
  204. targetObject, targetProperty, sourceProperty,
  205. bidirectional, options, reverseOptions
  206. ) {
  207. // do nothing if no model is set
  208. if (this.getModel() == null) {
  209. return;
  210. }
  211. // create the binding
  212. var id = this.getModel().bind(
  213. sourceProperty, targetObject, targetProperty, options
  214. );
  215. // create the reverse binding if necessary
  216. var idReverse = null
  217. if (bidirectional) {
  218. idReverse = targetObject.bind(
  219. targetProperty, this.getModel(), sourceProperty, reverseOptions
  220. );
  221. }
  222. // save the binding
  223. var targetHash = targetObject.toHashCode();
  224. if (this.__bindings[targetHash] == undefined) {
  225. this.__bindings[targetHash] = [];
  226. }
  227. this.__bindings[targetHash].push(
  228. [id, idReverse, targetProperty, sourceProperty, options, reverseOptions]
  229. );
  230. },
  231. /**
  232. * Removes the target identified by the three properties.
  233. *
  234. * @param targetObject {qx.core.Object} The target object on which the
  235. * binding exist.
  236. *
  237. * @param targetProperty {String} The targets property name used by the
  238. * adding of the target.
  239. *
  240. * @param sourceProperty {String} The name of the property of the model.
  241. */
  242. removeTarget: function(targetObject, targetProperty, sourceProperty) {
  243. this.__removeTargetFrom(
  244. targetObject, targetProperty, sourceProperty, this.getModel()
  245. );
  246. // delete the target in the targets reference
  247. for (var i = 0; i < this.__targets.length; i++) {
  248. if (
  249. this.__targets[i][0] == targetObject
  250. && this.__targets[i][1] == targetProperty
  251. && this.__targets[i][2] == sourceProperty
  252. ) {
  253. this.__targets.splice(i, 1);
  254. }
  255. }
  256. },
  257. /**
  258. * Does the work for {@link #removeTarget} but without removing the target
  259. * from the internal registry.
  260. *
  261. * @param targetObject {qx.core.Object} The target object on which the
  262. * binding exist.
  263. *
  264. * @param targetProperty {String} The targets property name used by the
  265. * adding of the target.
  266. *
  267. * @param sourceProperty {String} The name of the property of the model.
  268. *
  269. * @param sourceObject {String} The source object from which the binding
  270. * comes.
  271. */
  272. __removeTargetFrom: function(
  273. targetObject, targetProperty, sourceProperty, sourceObject
  274. ) {
  275. // check for not fitting targetObjects
  276. if (!(targetObject instanceof qx.core.Object)) {
  277. // just do nothing
  278. return;
  279. }
  280. var currentListing = this.__bindings[targetObject.toHashCode()];
  281. // if no binding is stored
  282. if (currentListing == undefined || currentListing.length == 0) {
  283. return;
  284. }
  285. // go threw all listings for the object
  286. for (var i = 0; i < currentListing.length; i++) {
  287. // if it is the listing
  288. if (
  289. currentListing[i][2] == targetProperty &&
  290. currentListing[i][3] == sourceProperty
  291. ) {
  292. // remove the binding
  293. var id = currentListing[i][0];
  294. sourceObject.removeBinding(id);
  295. // check for the reverse binding
  296. if (currentListing[i][1] != null) {
  297. targetObject.removeBinding(currentListing[i][1]);
  298. }
  299. // delete the entry and return
  300. currentListing.splice(i, 1);
  301. return;
  302. }
  303. }
  304. }
  305. },
  306. /*
  307. *****************************************************************************
  308. DESTRUCT
  309. *****************************************************************************
  310. */
  311. destruct : function() {
  312. // set the model to null to get the bindings removed
  313. if (this.getModel() != null && !this.getModel().isDisposed()) {
  314. this.setModel(null);
  315. }
  316. }
  317. });