Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / toolbar / Toolbar.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  * Basic Toolbar class. Although the {@link Ext.container.Container#defaultType defaultType} for Toolbar is {@link Ext.button.Button button}, Toolbar
17  * elements (child items for the Toolbar container) may be virtually any type of Component. Toolbar elements can be created explicitly via their
18  * constructors, or implicitly via their xtypes, and can be {@link #add}ed dynamically.
19  *
20  * ## Some items have shortcut strings for creation:
21  *
22  * | Shortcut | xtype         | Class                         | Description
23  * |:---------|:--------------|:------------------------------|:---------------------------------------------------
24  * | `->`     | `tbfill`      | {@link Ext.toolbar.Fill}      | begin using the right-justified button container
25  * | `-`      | `tbseparator` | {@link Ext.toolbar.Separator} | add a vertical separator bar between toolbar items
26  * | ` `      | `tbspacer`    | {@link Ext.toolbar.Spacer}    | add horiztonal space between elements
27  *
28  *     @example
29  *     Ext.create('Ext.toolbar.Toolbar', {
30  *         renderTo: document.body,
31  *         width   : 500,
32  *         items: [
33  *             {
34  *                 // xtype: 'button', // default for Toolbars
35  *                 text: 'Button'
36  *             },
37  *             {
38  *                 xtype: 'splitbutton',
39  *                 text : 'Split Button'
40  *             },
41  *             // begin using the right-justified button container
42  *             '->', // same as { xtype: 'tbfill' }
43  *             {
44  *                 xtype    : 'textfield',
45  *                 name     : 'field1',
46  *                 emptyText: 'enter search term'
47  *             },
48  *             // add a vertical separator bar between toolbar items
49  *             '-', // same as {xtype: 'tbseparator'} to create Ext.toolbar.Separator
50  *             'text 1', // same as {xtype: 'tbtext', text: 'text1'} to create Ext.toolbar.TextItem
51  *             { xtype: 'tbspacer' },// same as ' ' to create Ext.toolbar.Spacer
52  *             'text 2',
53  *             { xtype: 'tbspacer', width: 50 }, // add a 50px space
54  *             'text 3'
55  *         ]
56  *     });
57  *
58  * Toolbars have {@link #enable} and {@link #disable} methods which when called, will enable/disable all items within your toolbar.
59  *
60  *     @example
61  *     Ext.create('Ext.toolbar.Toolbar', {
62  *         renderTo: document.body,
63  *         width   : 400,
64  *         items: [
65  *             {
66  *                 text: 'Button'
67  *             },
68  *             {
69  *                 xtype: 'splitbutton',
70  *                 text : 'Split Button'
71  *             },
72  *             '->',
73  *             {
74  *                 xtype    : 'textfield',
75  *                 name     : 'field1',
76  *                 emptyText: 'enter search term'
77  *             }
78  *         ]
79  *     });
80  *
81  * Example
82  *
83  *     @example
84  *     var enableBtn = Ext.create('Ext.button.Button', {
85  *         text    : 'Enable All Items',
86  *         disabled: true,
87  *         scope   : this,
88  *         handler : function() {
89  *             //disable the enable button and enable the disable button
90  *             enableBtn.disable();
91  *             disableBtn.enable();
92  *
93  *             //enable the toolbar
94  *             toolbar.enable();
95  *         }
96  *     });
97  *
98  *     var disableBtn = Ext.create('Ext.button.Button', {
99  *         text    : 'Disable All Items',
100  *         scope   : this,
101  *         handler : function() {
102  *             //enable the enable button and disable button
103  *             disableBtn.disable();
104  *             enableBtn.enable();
105  *
106  *             //disable the toolbar
107  *             toolbar.disable();
108  *         }
109  *     });
110  *
111  *     var toolbar = Ext.create('Ext.toolbar.Toolbar', {
112  *         renderTo: document.body,
113  *         width   : 400,
114  *         margin  : '5 0 0 0',
115  *         items   : [enableBtn, disableBtn]
116  *     });
117  *
118  * Adding items to and removing items from a toolbar is as simple as calling the {@link #add} and {@link #remove} methods. There is also a {@link #removeAll} method
119  * which remove all items within the toolbar.
120  *
121  *     @example
122  *     var toolbar = Ext.create('Ext.toolbar.Toolbar', {
123  *         renderTo: document.body,
124  *         width   : 700,
125  *         items: [
126  *             {
127  *                 text: 'Example Button'
128  *             }
129  *         ]
130  *     });
131  *
132  *     var addedItems = [];
133  *
134  *     Ext.create('Ext.toolbar.Toolbar', {
135  *         renderTo: document.body,
136  *         width   : 700,
137  *         margin  : '5 0 0 0',
138  *         items   : [
139  *             {
140  *                 text   : 'Add a button',
141  *                 scope  : this,
142  *                 handler: function() {
143  *                     var text = prompt('Please enter the text for your button:');
144  *                     addedItems.push(toolbar.add({
145  *                         text: text
146  *                     }));
147  *                 }
148  *             },
149  *             {
150  *                 text   : 'Add a text item',
151  *                 scope  : this,
152  *                 handler: function() {
153  *                     var text = prompt('Please enter the text for your item:');
154  *                     addedItems.push(toolbar.add(text));
155  *                 }
156  *             },
157  *             {
158  *                 text   : 'Add a toolbar seperator',
159  *                 scope  : this,
160  *                 handler: function() {
161  *                     addedItems.push(toolbar.add('-'));
162  *                 }
163  *             },
164  *             {
165  *                 text   : 'Add a toolbar spacer',
166  *                 scope  : this,
167  *                 handler: function() {
168  *                     addedItems.push(toolbar.add('->'));
169  *                 }
170  *             },
171  *             '->',
172  *             {
173  *                 text   : 'Remove last inserted item',
174  *                 scope  : this,
175  *                 handler: function() {
176  *                     if (addedItems.length) {
177  *                         toolbar.remove(addedItems.pop());
178  *                     } else if (toolbar.items.length) {
179  *                         toolbar.remove(toolbar.items.last());
180  *                     } else {
181  *                         alert('No items in the toolbar');
182  *                     }
183  *                 }
184  *             },
185  *             {
186  *                 text   : 'Remove all items',
187  *                 scope  : this,
188  *                 handler: function() {
189  *                     toolbar.removeAll();
190  *                 }
191  *             }
192  *         ]
193  *     });
194  *
195  * @constructor
196  * Creates a new Toolbar
197  * @param {Object/Object[]} config A config object or an array of buttons to <code>{@link #add}</code>
198  * @docauthor Robert Dougan <rob@sencha.com>
199  */
200 Ext.define('Ext.toolbar.Toolbar', {
201     extend: 'Ext.container.Container',
202     requires: [
203         'Ext.toolbar.Fill',
204         'Ext.layout.container.HBox',
205         'Ext.layout.container.VBox',
206         'Ext.FocusManager'
207     ],
208     uses: [
209         'Ext.toolbar.Separator'
210     ],
211     alias: 'widget.toolbar',
212     alternateClassName: 'Ext.Toolbar',
213
214     isToolbar: true,
215     baseCls  : Ext.baseCSSPrefix + 'toolbar',
216     ariaRole : 'toolbar',
217
218     defaultType: 'button',
219
220     /**
221      * @cfg {Boolean} vertical
222      * Set to `true` to make the toolbar vertical. The layout will become a `vbox`.
223      */
224     vertical: false,
225
226     /**
227      * @cfg {String/Object} layout
228      * This class assigns a default layout (`layout: 'hbox'`).
229      * Developers _may_ override this configuration option if another layout
230      * is required (the constructor must be passed a configuration object in this
231      * case instead of an array).
232      * See {@link Ext.container.Container#layout} for additional information.
233      */
234
235     /**
236      * @cfg {Boolean} enableOverflow
237      * Configure true to make the toolbar provide a button which activates a dropdown Menu to show
238      * items which overflow the Toolbar's width.
239      */
240     enableOverflow: false,
241
242     /**
243      * @cfg {String} menuTriggerCls
244      * Configure the icon class of the overflow button.
245      */
246     menuTriggerCls: Ext.baseCSSPrefix + 'toolbar-more-icon',
247     
248     // private
249     trackMenus: true,
250
251     itemCls: Ext.baseCSSPrefix + 'toolbar-item',
252
253     initComponent: function() {
254         var me = this,
255             keys;
256
257         // check for simplified (old-style) overflow config:
258         if (!me.layout && me.enableOverflow) {
259             me.layout = { overflowHandler: 'Menu' };
260         }
261
262         if (me.dock === 'right' || me.dock === 'left') {
263             me.vertical = true;
264         }
265
266         me.layout = Ext.applyIf(Ext.isString(me.layout) ? {
267             type: me.layout
268         } : me.layout || {}, {
269             type: me.vertical ? 'vbox' : 'hbox',
270             align: me.vertical ? 'stretchmax' : 'middle',
271             clearInnerCtOnLayout: true
272         });
273
274         if (me.vertical) {
275             me.addClsWithUI('vertical');
276         }
277
278         // @TODO: remove this hack and implement a more general solution
279         if (me.ui === 'footer') {
280             me.ignoreBorderManagement = true;
281         }
282
283         me.callParent();
284
285         /**
286          * @event overflowchange
287          * Fires after the overflow state has changed.
288          * @param {Object} c The Container
289          * @param {Boolean} lastOverflow overflow state
290          */
291         me.addEvents('overflowchange');
292
293         // Subscribe to Ext.FocusManager for key navigation
294         keys = me.vertical ? ['up', 'down'] : ['left', 'right'];
295         Ext.FocusManager.subscribe(me, {
296             keys: keys
297         });
298     },
299
300     getRefItems: function(deep) {
301         var me = this,
302             items = me.callParent(arguments),
303             layout = me.layout,
304             handler;
305
306         if (deep && me.enableOverflow) {
307             handler = layout.overflowHandler;
308             if (handler && handler.menu) {
309                 items = items.concat(handler.menu.getRefItems(deep));
310             }
311         }
312         return items;
313     },
314
315     /**
316      * Adds element(s) to the toolbar -- this function takes a variable number of
317      * arguments of mixed type and adds them to the toolbar.
318      *
319      * **Note**: See the notes within {@link Ext.container.Container#add}.
320      *
321      * @param {Object...} args The following types of arguments are all valid:
322      *  - `{@link Ext.button.Button config}`: A valid button config object
323      *  - `HtmlElement`: Any standard HTML element
324      *  - `Field`: Any form field
325      *  - `Item`: Any subclass of {@link Ext.toolbar.Item}
326      *  - `String`: Any generic string (gets wrapped in a {@link Ext.toolbar.TextItem}).
327      *  Note that there are a few special strings that are treated differently as explained next.
328      *  - `'-'`: Creates a separator element
329      *  - `' '`: Creates a spacer element
330      *  - `'->'`: Creates a fill element
331      *
332      * @method add
333      */
334
335     // private
336     lookupComponent: function(c) {
337         if (Ext.isString(c)) {
338             var shortcut = Ext.toolbar.Toolbar.shortcuts[c];
339             if (shortcut) {
340                 c = {
341                     xtype: shortcut
342                 };
343             } else {
344                 c = {
345                     xtype: 'tbtext',
346                     text: c
347                 };
348             }
349             this.applyDefaults(c);
350         }
351         return this.callParent(arguments);
352     },
353
354     // private
355     applyDefaults: function(c) {
356         if (!Ext.isString(c)) {
357             c = this.callParent(arguments);
358             var d = this.internalDefaults;
359             if (c.events) {
360                 Ext.applyIf(c.initialConfig, d);
361                 Ext.apply(c, d);
362             } else {
363                 Ext.applyIf(c, d);
364             }
365         }
366         return c;
367     },
368
369     // private
370     trackMenu: function(item, remove) {
371         if (this.trackMenus && item.menu) {
372             var method = remove ? 'mun' : 'mon',
373                 me = this;
374
375             me[method](item, 'mouseover', me.onButtonOver, me);
376             me[method](item, 'menushow', me.onButtonMenuShow, me);
377             me[method](item, 'menuhide', me.onButtonMenuHide, me);
378         }
379     },
380
381     // private
382     constructButton: function(item) {
383         return item.events ? item : this.createComponent(item, item.split ? 'splitbutton' : this.defaultType);
384     },
385
386     // private
387     onBeforeAdd: function(component) {
388         if (component.is('field') || (component.is('button') && this.ui != 'footer')) {
389             component.ui = component.ui + '-toolbar';
390         }
391
392         // Any separators needs to know if is vertical or not
393         if (component instanceof Ext.toolbar.Separator) {
394             component.setUI((this.vertical) ? 'vertical' : 'horizontal');
395         }
396
397         this.callParent(arguments);
398     },
399
400     // private
401     onAdd: function(component) {
402         this.callParent(arguments);
403
404         this.trackMenu(component);
405         if (this.disabled) {
406             component.disable();
407         }
408     },
409
410     // private
411     onRemove: function(c) {
412         this.callParent(arguments);
413         this.trackMenu(c, true);
414     },
415
416     // private
417     onButtonOver: function(btn){
418         if (this.activeMenuBtn && this.activeMenuBtn != btn) {
419             this.activeMenuBtn.hideMenu();
420             btn.showMenu();
421             this.activeMenuBtn = btn;
422         }
423     },
424
425     // private
426     onButtonMenuShow: function(btn) {
427         this.activeMenuBtn = btn;
428     },
429
430     // private
431     onButtonMenuHide: function(btn) {
432         delete this.activeMenuBtn;
433     }
434 }, function() {
435     this.shortcuts = {
436         '-' : 'tbseparator',
437         ' ' : 'tbspacer',
438         '->': 'tbfill'
439     };
440 });