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