Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / button / Cycle.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * @class Ext.button.Cycle
17  * @extends Ext.button.Split
18  * A specialized SplitButton that contains a menu of {@link Ext.menu.CheckItem} elements.  The button automatically
19  * cycles through each menu item on click, raising the button's {@link #change} event (or calling the button's
20  * {@link #changeHandler} function, if supplied) for the active menu item. Clicking on the arrow section of the
21  * button displays the dropdown menu just like a normal SplitButton.  
22  * {@img Ext.button.Cycle/Ext.button.Cycle.png Ext.button.Cycle component}
23  * Example usage:
24  * <pre><code>
25 Ext.create('Ext.button.Cycle', {
26     showText: true,
27     prependText: 'View as ',
28     renderTo: Ext.getBody(),
29     menu: {
30         id: 'view-type-menu',
31         items: [{
32             text:'text only',
33             iconCls:'view-text',
34             checked:true
35         },{
36             text:'HTML',
37             iconCls:'view-html'
38         }]
39     },
40     changeHandler:function(cycleBtn, activeItem){
41         Ext.Msg.alert('Change View', activeItem.text);
42     }
43 });
44 </code></pre>
45  */
46 Ext.define('Ext.button.Cycle', {
47
48     /* Begin Definitions */
49
50     alias: 'widget.cycle',
51
52     extend: 'Ext.button.Split',
53     alternateClassName: 'Ext.CycleButton',
54
55     /* End Definitions */
56
57     /**
58      * @cfg {Array} items <p>Deprecated as of 4.0. Use the {@link #menu} config instead. All menu items will be created
59      * as {@link Ext.menu.CheckItem CheckItem}s.</p>
60      * <p>An array of {@link Ext.menu.CheckItem} <b>config</b> objects to be used when creating the
61      * button's menu items (e.g., {text:'Foo', iconCls:'foo-icon'})
62      */
63     /**
64      * @cfg {Boolean} showText True to display the active item's text as the button text (defaults to false).
65      * The Button will show its configured {@link #text} if this. config is omitted.
66      */
67     /**
68      * @cfg {String} prependText A static string to prepend before the active item's text when displayed as the
69      * button's text (only applies when showText = true, defaults to '')
70      */
71     /**
72      * @cfg {Function} changeHandler A callback function that will be invoked each time the active menu
73      * item in the button's menu has changed.  If this callback is not supplied, the SplitButton will instead
74      * fire the {@link #change} event on active item change.  The changeHandler function will be called with the
75      * following argument list: (SplitButton this, Ext.menu.CheckItem item)
76      */
77     /**
78      * @cfg {String} forceIcon A css class which sets an image to be used as the static icon for this button.  This
79      * icon will always be displayed regardless of which item is selected in the dropdown list.  This overrides the 
80      * default behavior of changing the button's icon to match the selected item's icon on change.
81      */
82     /**
83      * @property menu
84      * @type Menu
85      * The {@link Ext.menu.Menu Menu} object used to display the {@link Ext.menu.CheckItem CheckItems} representing the available choices.
86      */
87
88     // private
89     getButtonText: function(item) {
90         var me = this,
91             text = '';
92
93         if (item && me.showText === true) {
94             if (me.prependText) {
95                 text += me.prependText;
96             }
97             text += item.text;
98             return text;
99         }
100         return me.text;
101     },
102
103     /**
104      * Sets the button's active menu item.
105      * @param {Ext.menu.CheckItem} item The item to activate
106      * @param {Boolean} suppressEvent True to prevent the button's change event from firing (defaults to false)
107      */
108     setActiveItem: function(item, suppressEvent) {
109         var me = this;
110
111         if (!Ext.isObject(item)) {
112             item = me.menu.getComponent(item);
113         }
114         if (item) {
115             if (!me.rendered) {
116                 me.text = me.getButtonText(item);
117                 me.iconCls = item.iconCls;
118             } else {
119                 me.setText(me.getButtonText(item));
120                 me.setIconCls(item.iconCls);
121             }
122             me.activeItem = item;
123             if (!item.checked) {
124                 item.setChecked(true, false);
125             }
126             if (me.forceIcon) {
127                 me.setIconCls(me.forceIcon);
128             }
129             if (!suppressEvent) {
130                 me.fireEvent('change', me, item);
131             }
132         }
133     },
134
135     /**
136      * Gets the currently active menu item.
137      * @return {Ext.menu.CheckItem} The active item
138      */
139     getActiveItem: function() {
140         return this.activeItem;
141     },
142
143     // private
144     initComponent: function() {
145         var me = this,
146             checked = 0,
147             items;
148
149         me.addEvents(
150             /**
151              * @event change
152              * Fires after the button's active menu item has changed.  Note that if a {@link #changeHandler} function
153              * is set on this CycleButton, it will be called instead on active item change and this change event will
154              * not be fired.
155              * @param {Ext.button.Cycle} this
156              * @param {Ext.menu.CheckItem} item The menu item that was selected
157              */
158             "change"
159         );
160
161         if (me.changeHandler) {
162             me.on('change', me.changeHandler, me.scope || me);
163             delete me.changeHandler;
164         }
165
166         // Allow them to specify a menu config which is a standard Button config.
167         // Remove direct use of "items" in 5.0.
168         items = (me.menu.items||[]).concat(me.items||[]);
169         me.menu = Ext.applyIf({
170             cls: Ext.baseCSSPrefix + 'cycle-menu',
171             items: []
172         }, me.menu);
173
174         // Convert all items to CheckItems
175         Ext.each(items, function(item, i) {
176             item = Ext.applyIf({
177                 group: me.id,
178                 itemIndex: i,
179                 checkHandler: me.checkHandler,
180                 scope: me,
181                 checked: item.checked || false
182             }, item);
183             me.menu.items.push(item);
184             if (item.checked) {
185                 checked = i;
186             }
187         });
188         me.itemCount = me.menu.items.length;
189         me.callParent(arguments);
190         me.on('click', me.toggleSelected, me);
191         me.setActiveItem(checked, me);
192
193         // If configured with a fixed width, the cycling will center a different child item's text each click. Prevent this.
194         if (me.width && me.showText) {
195             me.addCls(Ext.baseCSSPrefix + 'cycle-fixed-width');
196         }
197     },
198
199     // private
200     checkHandler: function(item, pressed) {
201         if (pressed) {
202             this.setActiveItem(item);
203         }
204     },
205
206     /**
207      * This is normally called internally on button click, but can be called externally to advance the button's
208      * active item programmatically to the next one in the menu.  If the current item is the last one in the menu
209      * the active item will be set to the first item in the menu.
210      */
211     toggleSelected: function() {
212         var me = this,
213             m = me.menu,
214             checkItem;
215
216         checkItem = me.activeItem.next(':not([disabled])') || m.items.getAt(0);
217         checkItem.setChecked(true);
218     }
219 });