1 jls.loader.provide('jls.gui.Element');
  2 
  3 jls.loader.require('jls.gui.Style');
  4 jls.loader.require('jls.gui.FlowLayout');
  5 
  6 jls.gui.Element = jls.lang.Class.create( /** @lends jls.gui.Element.prototype */
  7 {
  8     /**
  9      * Creates an element.
 10      * 
 11      * @param {Object} [parameters] The element parameter.
 12      * @param {jls.gui.Element} [parent] The parent.
 13      * @constructs
 14 	 * @class This class represents a graphical element.
 15      */
 16     initialize : function(parameters, parent) {
 17         parameters = parameters || {};
 18         this._children = [];
 19         this._parent = parent || null;
 20         this._eventsHandlers = {};
 21         // Default parameters
 22         this._title = undefined;
 23         this._id = undefined;
 24         // Create style
 25         this._style = new jls.gui.Style(parameters.style);
 26         // Register style handler
 27     	this.getStyle().setHandler(this.onStyleChange.bind(this));
 28         if (this._parent) {
 29         	this._style.setParent(this._parent.getStyle());
 30         }
 31         // Create default layout
 32     	this._layout = this.createDefaultLayout();
 33     	// Set bounds
 34         this._x = this._style.getPropertyValue('left');
 35         this._y = this._style.getPropertyValue('top');
 36         this._w = this._style.getPropertyValue('width');
 37         this._h = this._style.getPropertyValue('height');
 38         // Set the attributes
 39         // TODO is this the right place?
 40         if (parameters.attributes) {
 41             this.setAttributes(parameters.attributes);
 42         }
 43         // Add this element to its parent
 44         // TODO is this the right place?
 45         if (this._parent != null) {
 46             this._parent.addChild(this);
 47         }
 48         // Create the concrete element
 49         this.onCreate();
 50         // Activate the style properties
 51         this._style.activate();
 52         // Add the children
 53         if (Object.isArray(parameters.children)) {
 54             this.addChildren(parameters.children);
 55         }
 56         if (this._layout != null) {
 57         	this._layout.onCreate();
 58         }
 59     },
 60     onCreate : jls.lang.Class.emptyFunction,
 61     getParent : function() {
 62         return this._parent;
 63     },
 64     /*
 65      * Attributes
 66      */
 67     /**
 68      * Sets an attribute.
 69      *
 70      * @param {String} key The attribute key.
 71      * @param {String} value The attribute value.
 72      */
 73     setAttribute : function(key, value) {
 74         /* look for a setter method in this */
 75         var setter = key.camelize('set');
 76         jls.logger.trace('setter: "' + setter + '"');
 77         if (Object.isFunction(this[setter])) {
 78             this[setter](value);
 79             return this;
 80         }
 81         this._setAttribute(key, value);
 82         return this;
 83     },
 84     _setAttribute : jls.lang.Class.emptyFunction,
 85     /**
 86      * Returns an attribute.
 87      *
 88      * @param {String} key The attribute key.
 89      * @returns {String} The attribute value.
 90      */
 91     getAttribute : function(key) {
 92         jls.logger.trace('getAttribute(' + key + ')');
 93         /* look for a getter method in this */
 94         var getter = key.camelize('get');
 95         jls.logger.trace('getter: "' + getter + '"');
 96         if (Object.isFunction(this[getter])) {
 97             return this[getter]();
 98         }
 99         return this._getAttribute(key);
100     },
101     _getAttribute : jls.lang.Class.emptyFunction,
102     /**
103      * Sets some attributes.
104      *
105      * @param {Object} attributes The attributes to set.
106      */
107     setAttributes : function(attributes) {
108         for ( var k in attributes) {
109             this.setAttribute(k, attributes[k]);
110         }
111         return this;
112     },
113     /**
114      * Returns the element style.
115      * 
116      * @returns {jls.gui.Style} The element style.
117      */
118     getStyle : function() {
119         return this._style;
120     },
121     // onStyleChange(key, oldValue, newValue)
122     onStyleChange : jls.lang.Class.emptyFunction,
123     /**
124      * Returns the layout associated with this element.
125      * 
126      * @returns {jls.gui.Layout} The element layout or null if there is no layout.
127      */
128     getLayout : function() {
129         return this._layout;
130     },
131     createDefaultLayout : function() {
132         return null;
133     },
134     /**
135      * Sets the layout for this element.
136      * 
137      * @param {jls.gui.Layout} layout The layout to associate to this element.
138      */
139     setLayout : function(layout) {
140     	if (layout != null) {
141     		if (typeof layout == 'string') {
142     			var layoutClass = jls.loader.require(layout);
143     			layout = new layoutClass(this);
144     		} else if (layout instanceof jls.gui.Layout) {
145     			layout.setElement(this);
146     		} else {
147     			throw new jls.lang.Exception('Invalid layout');
148     		}
149     	}
150         this._layout = layout;
151         return this;
152     },
153     /*
154      * Element hierarchy
155      */
156     /**
157      * Adds a child.
158      *
159      * @param {Object} child The child to add.
160      */
161     addChild : function(child) {
162         if (child instanceof jls.gui.Element) {
163             jls.logger.trace('addChild for jls.gui.Element');
164             // TODO if the child already have a parent remove it
165             child._parent = this;
166             this._children.push(child);
167         } else if ((typeof child == "object") && ('classname' in child)) {
168             jls.logger.trace('addChild for object');
169             child = jls.lang.Class.newInstance(child, this);
170         } else {
171             throw new jls.lang.Exception('don\'t know how to add "' + (typeof child) + '" child type');
172         }
173         if (this._layout != null) {
174         	this._layout.onAddChild(child);
175         }
176         return child;
177     },
178     /**
179      * Adds children.
180      *
181      * @param {Object} children The children to add.
182      */
183     addChildren : function(children) {
184         for ( var i = 0; i < children.length; i++) {
185             this.addChild(children[i]);
186         }
187         return this;
188     },
189     /**
190      * Removes a child.
191      *
192      * @param {Object} child The child to remove.
193      */
194     removeChild : function(child) {
195         var index = 0;
196         if (typeof child == 'number') {
197             index = child;
198         } else {
199             for (index = this._children.length - 1; (index >= 0) && (this._children[index] !== child); index--);
200             if (index < 0) {
201                 throw new jls.lang.Exception('Child not found');
202             }
203         }
204         var removed = this._children[index];
205         this._children.splice(index, 1);
206         //jls.logger.warn('removeChild(' + index + '))');
207         if (removed && (removed instanceof jls.gui.Element)) {
208             removed._parent = null;
209             removed.onDestroy();
210         }
211         if (this._layout != null) {
212         	this._layout.onRemoveChild(child);
213         }
214         return removed;
215     },
216     /**
217      * Removes all children.
218      *
219      */
220     removeChildren : function() {
221         for ( var i = this._children.length - 1; i >= 0; i--) {
222             this.removeChild(i);
223         }
224     },
225     /**
226      * Returns all children.
227      *
228      */
229     getChildren : function() {
230         return this._children;
231     },
232     getChildCount : function() {
233         return this._children.length;
234     },
235     /**
236      * Gets a child.
237      *
238      * @param {Number} index The child index.
239      * @returns {Object} The child.
240      */
241     getChild : function(index) {
242         return this._children[index];
243     },
244     /**
245      * Destroys this element.
246      *
247      */
248     destroy : function() {
249         if (this.getParent() != null) {
250             this.getParent().removeChild(this);
251         } else {
252             this.onDestroy();
253         }
254         return this;
255     },
256     onDestroy : jls.lang.Class.emptyFunction,
257     /*
258      * Event
259      */
260     dispatch : function(event) {
261         if (event.type in this._eventsHandlers) {
262             var handlers = this._eventsHandlers[event.type];
263             for (var i = handlers.length - 1; i >= 0; i--) {
264                 handlers[i](event);
265             }
266         }
267         return this;
268     },
269     onStartObserving: jls.lang.Class.emptyFunction,
270     onStopObserving: jls.lang.Class.emptyFunction,
271     /**
272      * Register an event handler.
273      *
274      * @param {String} type The event type to observe.
275      * @param {Function} handler The handler to register.
276      * @returns {Function} The handler.
277      */
278     observe: function(type, handler) {
279         if (Object.keys(this._eventsHandlers).length == 0) {
280             this.onStartObserving();
281         }
282         if (! (type in this._eventsHandlers)) {
283             this._eventsHandlers[type] = [];
284         }
285         this._eventsHandlers[type].push(handler);
286         return handler;
287     },
288     /**
289      * Unregister an event handler.
290      *
291      * @param {String} type The event type to unregister.
292      * @param {Function} handler The handler to unregister.
293      * @returns {Array} The removed handlers.
294      */
295     unobserve: function(type, handler) {
296         var removed = [];
297         if (type in this._eventsHandlers) {
298             var handlers = this._eventsHandlers[type];
299             for (var i = handlers.length - 1; i >= 0; i--) {
300                 if (handler == handlers[i]) {
301                     removed.push(handlers[i]);
302                     handlers.splice(i, 1);
303                 }
304             }
305             if (handlers.length == 0) {
306                 delete this._eventsHandlers[type];
307             }
308         }
309         if (Object.keys(this._eventsHandlers).length == 0) {
310             this.onStopObserving();
311         }
312         return removed;
313     },
314     /*
315      * Update
316      */
317     // TODO doLayout ?
318     update : function() {
319         if (this._layout != null) {
320         	this._layout.onUpdate();
321         }
322     },
323     /*
324      * Default attributes
325      */
326     getX : function() {
327         return this._x;
328     },
329     getY : function() {
330         return this._y;
331     },
332     getW : function() {
333         return this._w;
334     },
335     getH : function() {
336         return this._h;
337     },
338     getBounds : function() {
339         return [this._x, this._y, this._w, this._h];
340     },
341     setBounds : function(rect) {
342         this._x = rect[0];
343         this._y = rect[1];
344         this._w = rect[2];
345         this._h = rect[3];
346         return this;
347     },
348     getLocation : function() {
349         return [this._x, this._y];
350     },
351     setLocation : function(point) {
352         this._x = point[0];
353         this._y = point[1];
354         return this;
355     },
356     getSize : function() {
357         return [this._w, this._h];
358     },
359     setSize : function(dim) {
360         this._w = dim[0];
361         this._h = dim[1];
362         return this;
363     },
364     getClientOffset : function() {
365         return [0, 0];
366     },
367     /*getClientBounds : function() {
368         return this.getBounds();
369     },*/
370     getClientSize : function() {
371         return this.getSize();
372     },
373     getTitle : function() {
374         return this._title;
375     },
376     setTitle : function(title) {
377         this._title = title;
378         return this;
379     }
380 });
381 
382 Object.extend(jls.gui.Element, /** @lends jls.gui.Element */
383 {
384     /**
385      * Calls a function synchronously in the GUI message thread.
386      *
387      * @param {Function} fn The function to call.
388      * @returns {Object} The function result if any.
389      * @function
390      */
391     invokeAndWait : jls.win32.Window.invokeAndWait,
392     /**
393      * Calls a function asynchronously in the GUI message thread.
394      *
395      * @param {Function} fn The handler to unregister.
396      * @function
397      */
398     invokeLater : jls.win32.Window.invokeLater
399 });
400