Added wrt files, prior to converting into Qt/C++.
[ptas] / wrt / zouba / WRTKit / UI / SelectionList.js
1 /*\r
2 � Copyright 2008 Nokia Corporation. All rights reserved.\r
3 \r
4 IMPORTANT:  The Nokia software ("WRTKit and Example Widget files") is supplied to you by Nokia\r
5 Corporation (�Nokia�) in consideration of your agreement to the following terms. Your use, installation\r
6 and/or redistribution of the WRTKit and Example Widget files constitutes acceptance of these terms. If\r
7 you do not agree with these terms, please do not use, install, or redistribute the WRTKit and Example\r
8 Widget files.\r
9 \r
10 In consideration of your agreement to abide by the following terms, and subject to these terms, Nokia\r
11 grants you a personal, non-exclusive license, under Nokia�s copyrights in the WRTKit and Example\r
12 Widget files, to use, reproduce, and redistribute the WRTKit and Example files, in text form (for HTML,\r
13 CSS, or JavaScript files) or binary form (for associated images), for the sole purpose of creating S60\r
14 Widgets.\r
15 \r
16 If you redistribute the WRTKit and Example files, you must retain this entire notice in all such\r
17 redistributions of the WRTKit and Example files.\r
18 \r
19 You may not use the name, trademarks, service marks or logos of Nokia to endorse or promote products\r
20 that include the WRTKit and Example files without the prior written explicit agreement with Nokia.\r
21 Except as expressly stated in this notice, no other rights or licenses, express or implied, are granted by\r
22 Nokia herein, including but not limited to any patent rights that may be infringed by your products that\r
23 incorporate the WRTKit and Example files or by other works in which the WRTKit and Example files\r
24 may be incorporated.\r
25 \r
26 The WRTKit and Example files are provided on an "AS IS" basis.  NOKIA MAKES NO\r
27 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED\r
28 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A\r
29 PARTICULAR PURPOSE, REGARDING THE EXAMPLES OR ITS USE AND OPERATION\r
30 ALONE OR IN COMBINATION WITH YOUR PRODUCTS.\r
31 \r
32 IN NO EVENT SHALL NOKIA BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR\r
33 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
34 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
35 INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, AND/OR\r
36 DISTRIBUTION OF THE EXAMPLES, HOWEVER CAUSED AND WHETHER UNDER THEORY\r
37 OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE,\r
38 EVEN IF NOKIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
39 \r
40 */\r
41 \r
42 ///////////////////////////////////////////////////////////////////////////////\r
43 // The SelectionList class implements a single or multi selection control\r
44 // that lets users select one or more options from a list of options.\r
45 \r
46 // Constructor.\r
47 function SelectionList(id, caption, options, multipleSelection, selected) {\r
48     if (id != UI_NO_INIT_ID) {\r
49         this.init(id, caption, options, multipleSelection, selected);\r
50     }\r
51 }\r
52 \r
53 // SelectionList inherits from SelectionControl.\r
54 SelectionList.prototype = new SelectionControl(UI_NO_INIT_ID);\r
55 \r
56 // Root element for options.\r
57 SelectionList.prototype.optionListElement = null;\r
58 \r
59 // Array for tracking option elements.\r
60 SelectionList.prototype.optionElements = null;\r
61 \r
62 // Tracking for currently focused option; null if none.\r
63 SelectionList.prototype.focusedOption = null;\r
64 \r
65 // Enabled status.\r
66 SelectionList.prototype.enabled = false;\r
67 \r
68 // Initializer - called from constructor.\r
69 SelectionList.prototype.init = function(id, caption, options, multipleSelection, selected) {\r
70     uiLogger.debug("SelectionList.init(" + id + ", " + caption + ", " + options + ", " + multipleSelection + ", " + selected + ")");\r
71     \r
72     // call superclass initializer\r
73     SelectionControl.prototype.init.call(this, id, caption, options, multipleSelection, selected);\r
74     \r
75     // create option list element\r
76     this.optionListElement = document.createElement("div");\r
77     this.controlElement.appendChild(this.optionListElement);\r
78     \r
79     // the control defaults to enabled\r
80     this.enabled = true;\r
81     \r
82     // init option element arrays\r
83     this.optionElements = [];\r
84     \r
85     // update the option elements to match the options in this control\r
86     this.updateOptionElements();\r
87 }\r
88 \r
89 // Returns the enabled state.\r
90 SelectionList.prototype.isEnabled = function() {\r
91     return this.enabled;\r
92 }\r
93 \r
94 // Sets the enabled state.\r
95 SelectionList.prototype.setEnabled = function(enabled) {\r
96     uiLogger.debug("SelectionList.setEnabled(" + enabled + ")");\r
97     // switch the state and update the the control\r
98     this.enabled = enabled;\r
99     this.updateOptionElements();\r
100 }\r
101 \r
102 // Sets the focused state for the control.\r
103 // Note: This may not always succeed.\r
104 SelectionList.prototype.setFocused = function(focused) {\r
105     uiLogger.debug("SelectionList.setFocused(" + focused + ")");\r
106     if (this.enabled && this.optionElements.length > 0) {\r
107         if (focused) {\r
108             this.optionElements[0].link.focus();\r
109         } else {\r
110             this.optionElements[0].link.blur();\r
111         }\r
112     }\r
113 }\r
114 \r
115 // Sets the currently selected options. Pass a single option in a single selection\r
116 // control or an array of selected controls in a multiple selection control. To\r
117 // deselect all options pass null in a single selection control and an empty array\r
118 // in a multiple selection control.\r
119 SelectionList.prototype.setSelected = function(selected) {\r
120     // call superclass setSelected()\r
121     SelectionControl.prototype.setSelected.call(this, selected);\r
122     this.updateStyleFromState();\r
123 }\r
124 \r
125 // Sets the options in the control.\r
126 SelectionList.prototype.setOptions = function(options) {\r
127     // call superclass setOptions()\r
128     SelectionControl.prototype.setOptions.call(this, options);\r
129     this.updateOptionElements();\r
130 }\r
131 \r
132 // Updates the option elements for the control element.\r
133 SelectionList.prototype.updateOptionElements = function() {\r
134     uiLogger.debug("SelectionControl.updateOptionElements()");\r
135     \r
136     // start by removing all current options from the option list element\r
137     while (this.optionListElement.firstChild != null) {\r
138         this.optionListElement.removeChild(this.optionListElement.firstChild);\r
139     }\r
140     \r
141     // iterate through the options and add (and possibly create) a\r
142     // properly configured option element for each option\r
143     for (var i = 0; i < this.options.length; i++) {\r
144         // get the option and option element we're working on\r
145         var option = this.options[i];\r
146         \r
147         // option, link and text elements for this option\r
148         var optionElement;\r
149         var optionLinkElement;\r
150         var optionTextElement;\r
151         \r
152         // get the elements\r
153         if (i == this.optionElements.length) {\r
154             // we need to create a new option element...\r
155             optionElement = document.createElement("div");\r
156             \r
157             // ...and a new option link element...\r
158             optionLinkElement = document.createElement("a");\r
159             optionLinkElement.href = "JavaScript:void(0)";\r
160             \r
161             // ...and a new option text element\r
162             optionTextElement = document.createElement("span");\r
163             \r
164             // hook up event listeners to the element\r
165             var self = this;\r
166             optionLinkElement.addEventListener("focus", function(event) { self.optionFocusStateChanged(event, true); }, false);\r
167             optionLinkElement.addEventListener("blur", function(event) { self.optionFocusStateChanged(event, false); }, false);\r
168             optionElement.addEventListener("mouseover", function() { self.hoverStateChanged(true); }, false);\r
169             optionElement.addEventListener("mouseout", function() { self.hoverStateChanged(false); }, false);\r
170             optionElement.addEventListener("mousedown", function(event) {\r
171                                                                self.optionClicked(event)\r
172                                                                event.stopPropagation();\r
173                                                                event.preventDefault();\r
174                                                         }, true);\r
175             optionElement.addEventListener("keydown", function(event) {\r
176                                                             // center and enter trigger the action\r
177                                                             if (event.keyCode == 0 || event.keyCode == 13) {\r
178                                                                 self.optionClicked(event)\r
179                                                                 event.stopPropagation();\r
180                                                                 event.preventDefault();\r
181                                                             }\r
182                                                       }, true);\r
183             \r
184             // add the elements to the option element array\r
185             this.optionElements.push({ option: optionElement, link: optionLinkElement, text: optionTextElement });\r
186         } else {\r
187             // we already have ready elements so we'll reuse them\r
188             optionElement = this.optionElements[i].option;\r
189             optionLinkElement = this.optionElements[i].link;\r
190             optionTextElement = this.optionElements[i].text;\r
191             \r
192             // remove the option link element from its current parent - if any\r
193             if (optionLinkElement.parentNode != null) {\r
194                 optionLinkElement.parentNode.removeChild(optionLinkElement);\r
195             }\r
196             \r
197             // remove the option text element from its current parent - if any\r
198             if (optionTextElement.parentNode != null) {\r
199                 optionTextElement.parentNode.removeChild(optionTextElement);\r
200             }\r
201         }\r
202         \r
203         // set the option text\r
204         optionTextElement.innerHTML = option.text;\r
205         \r
206         // hook up the option to the control\r
207         if (this.enabled) {\r
208             // add the option link element to the option element\r
209             optionElement.appendChild(optionLinkElement);\r
210             // add the text element to the option element\r
211             optionLinkElement.appendChild(optionTextElement);\r
212         } else {\r
213             // add the text element directly to the control element\r
214             optionElement.appendChild(optionTextElement);\r
215         }\r
216         // add the option element to the option list element\r
217         this.optionListElement.appendChild(optionElement);\r
218     }\r
219     \r
220     // update the style\r
221     this.updateStyleFromState();\r
222 }\r
223 \r
224 // Callback for focus state change events.\r
225 SelectionList.prototype.optionFocusStateChanged = function(event, focused) {\r
226     uiLogger.debug("SelectionControl.optionFocusStateChanged()");\r
227     \r
228     // get the event source option\r
229     var option = null;\r
230     var optionElement = null;\r
231     for (var i = 0; i < this.optionElements.length; i++) {\r
232         optionElement = this.optionElements[i];\r
233         if (optionElement.link == event.currentTarget) {\r
234             option = this.options[i];\r
235             break;\r
236         }\r
237     }\r
238     \r
239     // remember the focused option; or null if none is focused\r
240     if (focused) {\r
241         this.focusedOption = option;\r
242     } else {\r
243         this.focusedOption = null;\r
244     }\r
245     \r
246     // call the superclass focus state change handler\r
247     this.focusStateChanged(focused);\r
248 }\r
249 \r
250 // Callback for clicks.\r
251 SelectionList.prototype.optionClicked = function(event) {\r
252     uiLogger.debug("SelectionControl.optionClicked()");\r
253     \r
254     // bail out if we're not enabled\r
255     if (!this.enabled) {\r
256         return false;\r
257     }\r
258     \r
259     // get the changed option\r
260     var option = null;\r
261     var optionElement = null;\r
262     for (var i = 0; i < this.optionElements.length; i++) {\r
263         optionElement = this.optionElements[i];\r
264         if (optionElement.option == event.currentTarget) {\r
265             option = this.options[i];\r
266             break;\r
267         }\r
268     }\r
269     \r
270     // make sure the option is focused\r
271     optionElement.link.focus();\r
272     \r
273     // toggle the selection\r
274     if (this.multipleSelection) {\r
275         // iterate through the selected options and see if this\r
276         // option is selected. if not then add it to the selection.\r
277         // if it already is selected then them remove it.\r
278         var found = false;\r
279         for (var i = 0; i < this.selected.length; i++) {\r
280             if (this.selected[i] == option) {\r
281                 // remove from selected set\r
282                 found = true;\r
283                 this.selected.splice(i, 1);\r
284                 break;\r
285             }\r
286         }\r
287         if (!found) {\r
288             // add to the selected set\r
289             this.selected.push(option);\r
290         }\r
291     } else {\r
292         // update the selected option\r
293         this.selected = option;\r
294     }\r
295     \r
296     // update the style\r
297     this.updateStyleFromState();\r
298     \r
299     // notify event listeners\r
300     this.fireEvent(this.createEvent("SelectionChanged", this.getSelected()));\r
301 }\r
302 \r
303 // Resets the state tracking for focus and hover.\r
304 // Override this in subclasses as required to implement the state reset.\r
305 SelectionList.prototype.resetFocusState = function() {\r
306     uiLogger.debug("SelectionList.resetFocusState()");\r
307     this.hovering = false;\r
308     this.focused = false;\r
309     this.focusedOption = null;\r
310     this.updateStyleFromState();\r
311 }\r
312 \r
313 // Updates the style of the control to reflects the state of the control.\r
314 SelectionList.prototype.updateStyleFromState = function() {\r
315     uiLogger.debug("SelectionList.updateStyleFromState()");\r
316     \r
317     // determine the state name\r
318     var stateName = this.getStyleStateName();\r
319     \r
320     // set element class names\r
321     this.setClassName(this.rootElement, "Control");\r
322     this.setClassName(this.controlElement, "ControlElement");\r
323     this.setClassName(this.assemblyElement, "ControlAssembly ControlAssembly" + stateName);\r
324     this.setClassName(this.captionElement, "ControlCaption ControlCaption" + stateName);\r
325     \r
326     // set option list and option class names\r
327     this.setClassName(this.optionListElement, "SelectionList SelectionList" + stateName);\r
328     for (var i = 0; i < this.options.length; i++) {\r
329         var option = this.options[i];\r
330         \r
331         // get the option and option text elements for this option\r
332         var optionElement = this.optionElements[i].option;\r
333         var optionTextElement = this.optionElements[i].text;\r
334         \r
335         // figure out the option state\r
336         var optionStateName = this.isSelected(option) ? "Checked" : "Unchecked";\r
337         if (!this.enabled) {\r
338             optionStateName += "Disabled";\r
339         } else if (this.focusedOption == option) {\r
340             optionStateName += "Focus";\r
341         } else {\r
342             optionStateName += "Normal";\r
343         }\r
344         \r
345         // set option element class names\r
346         if (this.multipleSelection) {\r
347             this.setClassName(optionElement, "SelectionListOptionMulti SelectionListOptionMulti" + optionStateName);\r
348         } else {\r
349             this.setClassName(optionElement, "SelectionListOptionSingle SelectionListOptionSingle" + optionStateName);\r
350         }\r
351         \r
352         // set option text class names\r
353         this.setClassName(optionTextElement, "SelectionListOptionText SelectionListOptionText" + stateName);\r
354     }\r
355 }\r