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