commit extjs-2.2.1
[extjs.git] / source / widgets / CycleButton.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.CycleButton\r
11  * @extends Ext.SplitButton\r
12  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically\r
13  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's\r
14  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the\r
15  * button displays the dropdown menu just like a normal SplitButton.  Example usage:\r
16  * <pre><code>\r
17 var btn = new Ext.CycleButton({\r
18     showText: true,\r
19     prependText: 'View as ',\r
20     items: [{\r
21         text:'text only',\r
22         iconCls:'view-text',\r
23         checked:true\r
24     },{\r
25         text:'HTML',\r
26         iconCls:'view-html'\r
27     }],\r
28     changeHandler:function(btn, item){\r
29         Ext.Msg.alert('Change View', item.text);\r
30     }\r
31 });\r
32 </code></pre>\r
33  * @constructor\r
34  * Create a new split button\r
35  * @param {Object} config The config object\r
36  */\r
37 Ext.CycleButton = Ext.extend(Ext.SplitButton, {\r
38     /**\r
39      * @cfg {Array} items An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the\r
40      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})\r
41      */\r
42     /**\r
43      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false)\r
44      */\r
45     /**\r
46      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the\r
47      * button's text (only applies when showText = true, defaults to '')\r
48      */\r
49     /**\r
50      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu\r
51      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead\r
52      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the\r
53      * following argument list: (SplitButton this, Ext.menu.CheckItem item)\r
54      */\r
55         /**\r
56          * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This\r
57          * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the \r
58          * default behavior of changing the button's icon to match the selected item's icon on change.\r
59          */\r
60 \r
61     // private\r
62     getItemText : function(item){\r
63         if(item && this.showText === true){\r
64             var text = '';\r
65             if(this.prependText){\r
66                 text += this.prependText;\r
67             }\r
68             text += item.text;\r
69             return text;\r
70         }\r
71         return undefined;\r
72     },\r
73 \r
74     /**\r
75      * Sets the button's active menu item.\r
76      * @param {Ext.menu.CheckItem} item The item to activate\r
77      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)\r
78      */\r
79     setActiveItem : function(item, suppressEvent){\r
80         if(typeof item != 'object'){\r
81             item = this.menu.items.get(item);\r
82         }\r
83         if(item){\r
84             if(!this.rendered){\r
85                 this.text = this.getItemText(item);\r
86                 this.iconCls = item.iconCls;\r
87             }else{\r
88                 var t = this.getItemText(item);\r
89                 if(t){\r
90                     this.setText(t);\r
91                 }\r
92                 this.setIconClass(item.iconCls);\r
93             }\r
94             this.activeItem = item;\r
95             if(!item.checked){\r
96                 item.setChecked(true, true);\r
97             }\r
98             if(this.forceIcon){\r
99                 this.setIconClass(this.forceIcon);\r
100             }\r
101             if(!suppressEvent){\r
102                 this.fireEvent('change', this, item);\r
103             }\r
104         }\r
105     },\r
106 \r
107     /**\r
108      * Gets the currently active menu item.\r
109      * @return {Ext.menu.CheckItem} The active item\r
110      */\r
111     getActiveItem : function(){\r
112         return this.activeItem;\r
113     },\r
114 \r
115     // private\r
116     initComponent : function(){\r
117         this.addEvents(\r
118             /**\r
119              * @event change\r
120              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function\r
121              * is set on this CycleButton, it will be called instead on active item change and this change event will\r
122              * not be fired.\r
123              * @param {Ext.CycleButton} this\r
124              * @param {Ext.menu.CheckItem} item The menu item that was selected\r
125              */\r
126             "change"\r
127         );\r
128 \r
129         if(this.changeHandler){\r
130             this.on('change', this.changeHandler, this.scope||this);\r
131             delete this.changeHandler;\r
132         }\r
133 \r
134         this.itemCount = this.items.length;\r
135 \r
136         this.menu = {cls:'x-cycle-menu', items:[]};\r
137         var checked;\r
138         for(var i = 0, len = this.itemCount; i < len; i++){\r
139             var item = this.items[i];\r
140             item.group = item.group || this.id;\r
141             item.itemIndex = i;\r
142             item.checkHandler = this.checkHandler;\r
143             item.scope = this;\r
144             item.checked = item.checked || false;\r
145             this.menu.items.push(item);\r
146             if(item.checked){\r
147                 checked = item;\r
148             }\r
149         }\r
150         this.setActiveItem(checked, true);\r
151         Ext.CycleButton.superclass.initComponent.call(this);\r
152 \r
153         this.on('click', this.toggleSelected, this);\r
154     },\r
155 \r
156     // private\r
157     checkHandler : function(item, pressed){\r
158         if(pressed){\r
159             this.setActiveItem(item);\r
160         }\r
161     },\r
162 \r
163     /**\r
164      * This is normally called internally on button click, but can be called externally to advance the button's\r
165      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu\r
166      * the active item will be set to the first item in the menu.\r
167      */\r
168     toggleSelected : function(){\r
169         this.menu.render();\r
170                 \r
171                 var nextIdx, checkItem;\r
172                 for (var i = 1; i < this.itemCount; i++) {\r
173                         nextIdx = (this.activeItem.itemIndex + i) % this.itemCount;\r
174                         // check the potential item\r
175                         checkItem = this.menu.items.itemAt(nextIdx);\r
176                         // if its not disabled then check it.\r
177                         if (!checkItem.disabled) {\r
178                                 checkItem.setChecked(true);\r
179                                 break;\r
180                         }\r
181                 }\r
182     }\r
183 });\r
184 Ext.reg('cycle', Ext.CycleButton);