/*!
* Ext JS Library 3.3.0
* Copyright(c) 2006-2010 Ext JS, Inc.
* licensing@extjs.com
* http://www.extjs.com/license
*/
/**
* @class Ext.layout.boxOverflow.Menu
* @extends Ext.layout.boxOverflow.None
* Description
*/
Ext.layout.boxOverflow.Menu = Ext.extend(Ext.layout.boxOverflow.None, {
/**
* @cfg afterCls
* @type String
* CSS class added to the afterCt element. This is the element that holds any special items such as scrollers,
* which must always be present at the rightmost edge of the Container
*/
afterCls: 'x-strip-right',
/**
* @property noItemsMenuText
* @type String
* HTML fragment to render into the toolbar overflow menu if there are no items to display
*/
noItemsMenuText : '(None)
',
constructor: function(layout) {
Ext.layout.boxOverflow.Menu.superclass.constructor.apply(this, arguments);
/**
* @property menuItems
* @type Array
* Array of all items that are currently hidden and should go into the dropdown menu
*/
this.menuItems = [];
},
/**
* @private
* Creates the beforeCt, innerCt and afterCt elements if they have not already been created
* @param {Ext.Container} container The Container attached to this Layout instance
* @param {Ext.Element} target The target Element
*/
createInnerElements: function() {
if (!this.afterCt) {
this.afterCt = this.layout.innerCt.insertSibling({cls: this.afterCls}, 'before');
}
},
/**
* @private
*/
clearOverflow: function(calculations, targetSize) {
var newWidth = targetSize.width + (this.afterCt ? this.afterCt.getWidth() : 0),
items = this.menuItems;
this.hideTrigger();
for (var index = 0, length = items.length; index < length; index++) {
items.pop().component.show();
}
return {
targetSize: {
height: targetSize.height,
width : newWidth
}
};
},
/**
* @private
*/
showTrigger: function() {
this.createMenu();
this.menuTrigger.show();
},
/**
* @private
*/
hideTrigger: function() {
if (this.menuTrigger != undefined) {
this.menuTrigger.hide();
}
},
/**
* @private
* Called before the overflow menu is shown. This constructs the menu's items, caching them for as long as it can.
*/
beforeMenuShow: function(menu) {
var items = this.menuItems,
len = items.length,
item,
prev;
var needsSep = function(group, item){
return group.isXType('buttongroup') && !(item instanceof Ext.Toolbar.Separator);
};
this.clearMenu();
menu.removeAll();
for (var i = 0; i < len; i++) {
item = items[i].component;
if (prev && (needsSep(item, prev) || needsSep(prev, item))) {
menu.add('-');
}
this.addComponentToMenu(menu, item);
prev = item;
}
// put something so the menu isn't empty if no compatible items found
if (menu.items.length < 1) {
menu.add(this.noItemsMenuText);
}
},
/**
* @private
* Returns a menu config for a given component. This config is used to create a menu item
* to be added to the expander menu
* @param {Ext.Component} component The component to create the config for
* @param {Boolean} hideOnClick Passed through to the menu item
*/
createMenuConfig : function(component, hideOnClick){
var config = Ext.apply({}, component.initialConfig),
group = component.toggleGroup;
Ext.copyTo(config, component, [
'iconCls', 'icon', 'itemId', 'disabled', 'handler', 'scope', 'menu'
]);
Ext.apply(config, {
text : component.overflowText || component.text,
hideOnClick: hideOnClick
});
if (group || component.enableToggle) {
Ext.apply(config, {
group : group,
checked: component.pressed,
listeners: {
checkchange: function(item, checked){
component.toggle(checked);
}
}
});
}
delete config.ownerCt;
delete config.xtype;
delete config.id;
return config;
},
/**
* @private
* Adds the given Toolbar item to the given menu. Buttons inside a buttongroup are added individually.
* @param {Ext.menu.Menu} menu The menu to add to
* @param {Ext.Component} component The component to add
*/
addComponentToMenu : function(menu, component) {
if (component instanceof Ext.Toolbar.Separator) {
menu.add('-');
} else if (Ext.isFunction(component.isXType)) {
if (component.isXType('splitbutton')) {
menu.add(this.createMenuConfig(component, true));
} else if (component.isXType('button')) {
menu.add(this.createMenuConfig(component, !component.menu));
} else if (component.isXType('buttongroup')) {
component.items.each(function(item){
this.addComponentToMenu(menu, item);
}, this);
}
}
},
/**
* @private
* Deletes the sub-menu of each item in the expander menu. Submenus are created for items such as
* splitbuttons and buttongroups, where the Toolbar item cannot be represented by a single menu item
*/
clearMenu : function(){
var menu = this.moreMenu;
if (menu && menu.items) {
menu.items.each(function(item){
delete item.menu;
});
}
},
/**
* @private
* Creates the overflow trigger and menu used when enableOverflow is set to true and the items
* in the layout are too wide to fit in the space available
*/
createMenu: function() {
if (!this.menuTrigger) {
this.createInnerElements();
/**
* @private
* @property menu
* @type Ext.menu.Menu
* The expand menu - holds items for every item that cannot be shown
* because the container is currently not large enough.
*/
this.menu = new Ext.menu.Menu({
ownerCt : this.layout.container,
listeners: {
scope: this,
beforeshow: this.beforeMenuShow
}
});
/**
* @private
* @property menuTrigger
* @type Ext.Button
* The expand button which triggers the overflow menu to be shown
*/
this.menuTrigger = new Ext.Button({
iconCls : 'x-toolbar-more-icon',
cls : 'x-toolbar-more',
menu : this.menu,
renderTo: this.afterCt
});
}
},
/**
* @private
*/
destroy: function() {
Ext.destroy(this.menu, this.menuTrigger);
}
});
Ext.layout.boxOverflow.menu = Ext.layout.boxOverflow.Menu;
/**
* @class Ext.layout.boxOverflow.HorizontalMenu
* @extends Ext.layout.boxOverflow.Menu
* Description
*/
Ext.layout.boxOverflow.HorizontalMenu = Ext.extend(Ext.layout.boxOverflow.Menu, {
constructor: function() {
Ext.layout.boxOverflow.HorizontalMenu.superclass.constructor.apply(this, arguments);
var me = this,
layout = me.layout,
origFunction = layout.calculateChildBoxes;
layout.calculateChildBoxes = function(visibleItems, targetSize) {
var calcs = origFunction.apply(layout, arguments),
meta = calcs.meta,
items = me.menuItems;
//calculate the width of the items currently hidden solely because there is not enough space
//to display them
var hiddenWidth = 0;
for (var index = 0, length = items.length; index < length; index++) {
hiddenWidth += items[index].width;
}
meta.minimumWidth += hiddenWidth;
meta.tooNarrow = meta.minimumWidth > targetSize.width;
return calcs;
};
},
handleOverflow: function(calculations, targetSize) {
this.showTrigger();
var newWidth = targetSize.width - this.afterCt.getWidth(),
boxes = calculations.boxes,
usedWidth = 0,
recalculate = false;
//calculate the width of all visible items and any spare width
for (var index = 0, length = boxes.length; index < length; index++) {
usedWidth += boxes[index].width;
}
var spareWidth = newWidth - usedWidth,
showCount = 0;
//see if we can re-show any of the hidden components
for (var index = 0, length = this.menuItems.length; index < length; index++) {
var hidden = this.menuItems[index],
comp = hidden.component,
width = hidden.width;
if (width < spareWidth) {
comp.show();
spareWidth -= width;
showCount ++;
recalculate = true;
} else {
break;
}
}
if (recalculate) {
this.menuItems = this.menuItems.slice(showCount);
} else {
for (var i = boxes.length - 1; i >= 0; i--) {
var item = boxes[i].component,
right = boxes[i].left + boxes[i].width;
if (right >= newWidth) {
this.menuItems.unshift({
component: item,
width : boxes[i].width
});
item.hide();
} else {
break;
}
}
}
if (this.menuItems.length == 0) {
this.hideTrigger();
}
return {
targetSize: {
height: targetSize.height,
width : newWidth
},
recalculate: recalculate
};
}
});
Ext.layout.boxOverflow.menu.hbox = Ext.layout.boxOverflow.HorizontalMenu;