Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / menu / Manager.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.menu.Manager
17  * Provides a common registry of all menus on a page.
18  * @singleton
19  */
20 Ext.define('Ext.menu.Manager', {
21     singleton: true,
22     requires: [
23         'Ext.util.MixedCollection',
24         'Ext.util.KeyMap'
25     ],
26     alternateClassName: 'Ext.menu.MenuMgr',
27
28     uses: ['Ext.menu.Menu'],
29
30     menus: {},
31     groups: {},
32     attached: false,
33     lastShow: new Date(),
34
35     init: function() {
36         var me = this;
37         
38         me.active = Ext.create('Ext.util.MixedCollection');
39         Ext.getDoc().addKeyListener(27, function() {
40             if (me.active.length > 0) {
41                 me.hideAll();
42             }
43         }, me);
44     },
45
46     /**
47      * Hides all menus that are currently visible
48      * @return {Boolean} success True if any active menus were hidden.
49      */
50     hideAll: function() {
51         var active = this.active,
52             c;
53         if (active && active.length > 0) {
54             c = active.clone();
55             c.each(function(m) {
56                 m.hide();
57             });
58             return true;
59         }
60         return false;
61     },
62
63     onHide: function(m) {
64         var me = this,
65             active = me.active;
66         active.remove(m);
67         if (active.length < 1) {
68             Ext.getDoc().un('mousedown', me.onMouseDown, me);
69             me.attached = false;
70         }
71     },
72
73     onShow: function(m) {
74         var me = this,
75             active   = me.active,
76             last     = active.last(),
77             attached = me.attached,
78             menuEl   = m.getEl(),
79             zIndex;
80
81         me.lastShow = new Date();
82         active.add(m);
83         if (!attached) {
84             Ext.getDoc().on('mousedown', me.onMouseDown, me);
85             me.attached = true;
86         }
87         m.toFront();
88     },
89
90     onBeforeHide: function(m) {
91         if (m.activeChild) {
92             m.activeChild.hide();
93         }
94         if (m.autoHideTimer) {
95             clearTimeout(m.autoHideTimer);
96             delete m.autoHideTimer;
97         }
98     },
99
100     onBeforeShow: function(m) {
101         var active = this.active,
102             parentMenu = m.parentMenu;
103             
104         active.remove(m);
105         if (!parentMenu && !m.allowOtherMenus) {
106             this.hideAll();
107         }
108         else if (parentMenu && parentMenu.activeChild && m != parentMenu.activeChild) {
109             parentMenu.activeChild.hide();
110         }
111     },
112
113     // private
114     onMouseDown: function(e) {
115         var me = this,
116             active = me.active,
117             lastShow = me.lastShow;
118
119         if (Ext.Date.getElapsed(lastShow) > 50 && active.length > 0 && !e.getTarget('.' + Ext.baseCSSPrefix + 'menu')) {
120             me.hideAll();
121         }
122     },
123
124     // private
125     register: function(menu) {
126         var me = this;
127
128         if (!me.active) {
129             me.init();
130         }
131
132         if (menu.floating) {
133             me.menus[menu.id] = menu;
134             menu.on({
135                 beforehide: me.onBeforeHide,
136                 hide: me.onHide,
137                 beforeshow: me.onBeforeShow,
138                 show: me.onShow,
139                 scope: me
140             });
141         }
142     },
143
144     /**
145      * Returns a {@link Ext.menu.Menu} object
146      * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
147      * be used to generate and return a new Menu this.
148      * @return {Ext.menu.Menu} The specified menu, or null if none are found
149      */
150     get: function(menu) {
151         var menus = this.menus;
152         
153         if (typeof menu == 'string') { // menu id
154             if (!menus) {  // not initialized, no menus to return
155                 return null;
156             }
157             return menus[menu];
158         } else if (menu.isMenu) {  // menu instance
159             return menu;
160         } else if (Ext.isArray(menu)) { // array of menu items
161             return Ext.create('Ext.menu.Menu', {items:menu});
162         } else { // otherwise, must be a config
163             return Ext.ComponentManager.create(menu, 'menu');
164         }
165     },
166
167     // private
168     unregister: function(menu) {
169         var me = this,
170             menus = me.menus,
171             active = me.active;
172
173         delete menus[menu.id];
174         active.remove(menu);
175         menu.un({
176             beforehide: me.onBeforeHide,
177             hide: me.onHide,
178             beforeshow: me.onBeforeShow,
179             show: me.onShow,
180             scope: me
181         });
182     },
183
184     // private
185     registerCheckable: function(menuItem) {
186         var groups  = this.groups,
187             groupId = menuItem.group;
188
189         if (groupId) {
190             if (!groups[groupId]) {
191                 groups[groupId] = [];
192             }
193
194             groups[groupId].push(menuItem);
195         }
196     },
197
198     // private
199     unregisterCheckable: function(menuItem) {
200         var groups  = this.groups,
201             groupId = menuItem.group;
202
203         if (groupId) {
204             Ext.Array.remove(groups[groupId], menuItem);
205         }
206     },
207
208     onCheckChange: function(menuItem, state) {
209         var groups  = this.groups,
210             groupId = menuItem.group,
211             i       = 0,
212             group, ln, curr;
213
214         if (groupId && state) {
215             group = groups[groupId];
216             ln = group.length;
217             for (; i < ln; i++) {
218                 curr = group[i];
219                 if (curr != menuItem) {
220                     curr.setChecked(false);
221                 }
222             }
223         }
224     }
225 });