    /**
     * @author Nicolas Ferrero
     * A TabPanel with grouping support.
     */
    Ext.define('Ext.ux.GroupTabPanel', {
        extend: 'Ext.Container',

        alias: 'widget.grouptabpanel',

        requires:[
            'Ext.tree.Panel',
            'Ext.ux.GroupTabRenderer'
        ],

        baseCls : Ext.baseCSSPrefix + 'grouptabpanel',

        initComponent: function(config) {
            var me = this;

            Ext.apply(me, config);

            // Processes items to create the TreeStore and also set up
            // "this.cards" containing the actual card items.
            me.store = me.createTreeStore();

            me.layout = {
                type: 'hbox',
                align: 'stretch'
            };
            me.defaults = {
                border: false
            };

            me.items = [{
                xtype: 'treepanel',
                cls: 'x-tree-panel x-grouptabbar',
                width: 150,
                rootVisible: false,
                store: me.store,
                hideHeaders: true,
                animate: false,
                processEvent: Ext.emptyFn,
                border: false,
                plugins: [{
                    ptype: 'grouptabrenderer'
                }],
                viewConfig: {
                    overItemCls: '',
                    getRowClass: me.getRowClass
                },
                columns: [{
                    xtype: 'treecolumn',
                    sortable: false,
                    dataIndex: 'text',
                    flex: 1,
                    renderer: function (value, cell, node, idx1, idx2, store, tree) {
                        var cls = '';

                        if (node.parentNode && node.parentNode.parentNode === null) {
                            cls += ' x-grouptab-first';
                            if (node.previousSibling) {
                                cls += ' x-grouptab-prev';
                            }
                            if (!node.get('expanded') || node.firstChild == null) {
                                cls += ' x-grouptab-last';
                            }
                        } else if (node.nextSibling === null) {
                            cls += ' x-grouptab-last';
                        } else {
                            cls += ' x-grouptab-center';
                        }
                        if (node.data.activeTab) {
                            cls += ' x-active-tab';
                        }
                        cell.tdCls= 'x-grouptab'+ cls;

                        return value;
                    }
                 }]
            }, {
                xtype: 'container',
                flex: 1,
                layout: 'card',
                activeItem: me.mainItem,
                baseCls: Ext.baseCSSPrefix + 'grouptabcontainer',
                items: me.cards
            }];

            me.addEvents(
                /**
                 * @event beforetabchange
                 * Fires before a tab change (activated by {@link #setActiveTab}). Return false in any listener to cancel
                 * the tabchange
                 * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
                 * @param {Ext.Component} newCard The card that is about to be activated
                 * @param {Ext.Component} oldCard The card that is currently active
                 */
                'beforetabchange',

                /**
                 * @event tabchange
                 * Fires when a new tab has been activated (activated by {@link #setActiveTab}).
                 * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
                 * @param {Ext.Component} newCard The newly activated item
                 * @param {Ext.Component} oldCard The previously active item
                 */
                'tabchange',

                /**
                 * @event beforegroupchange
                 * Fires before a group change (activated by {@link #setActiveGroup}). Return false in any listener to cancel
                 * the groupchange
                 * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
                 * @param {Ext.Component} newGroup The root group card that is about to be activated
                 * @param {Ext.Component} oldGroup The root group card that is currently active
                 */
                'beforegroupchange',

                /**
                 * @event groupchange
                 * Fires when a new group has been activated (activated by {@link #setActiveGroup}).
                 * @param {Ext.ux.GroupTabPanel} grouptabPanel The GroupTabPanel
                 * @param {Ext.Component} newGroup The newly activated root group item
                 * @param {Ext.Component} oldGroup The previously active root group item
                 */
                'groupchange'
            );

            me.callParent(arguments);
            me.setActiveTab(me.activeTab);
            me.setActiveGroup(me.activeGroup);
            me.mon(me.down('treepanel').getSelectionModel(), 'select', me.onNodeSelect, me);
        },

        getRowClass: function(node, rowIndex, rowParams, store) {
            var cls = '';
            if (node.data.activeGroup) {
               cls += ' x-active-group';
            }
            return cls;
        },

        /**
         * @private
         * Node selection listener.
         */
        onNodeSelect: function (selModel, node) {
            var me = this,
                currentNode = me.store.getRootNode(),
                parent;

            if (node.parentNode && node.parentNode.parentNode === null) {
                parent = node;
            } else {
                parent = node.parentNode;
            }

            if (me.setActiveGroup(parent.get('id')) === false || me.setActiveTab(node.get('id')) === false) {
                return false;
            }

            while (currentNode) {
                currentNode.set('activeTab', false);
                currentNode.set('activeGroup', false);
                currentNode = currentNode.firstChild || currentNode.nextSibling || currentNode.parentNode.nextSibling;
            }

            parent.set('activeGroup', true);
            parent.eachChild(function(child) {
                child.set('activeGroup', true);
            });
            node.set('activeTab', true);
            selModel.view.refresh();
        },

        /**
         * Makes the given component active (makes it the visible card in the GroupTabPanel's CardLayout)
         * @param {Ext.Component} cmp The component to make active
         */
        setActiveTab: function(cmp) {
            var me = this,
                newTab = cmp,
                oldTab;

            if(Ext.isString(cmp)) {
                newTab = Ext.getCmp(newTab);
            }

            if (newTab === me.activeTab) {
                return false;
            }

            oldTab = me.activeTab;
            if (me.fireEvent('beforetabchange', me, newTab, oldTab) !== false) {
                 me.activeTab = newTab;
                 if (me.rendered) {
                     me.down('container[baseCls=' + Ext.baseCSSPrefix + 'grouptabcontainer' + ']').getLayout().setActiveItem(newTab);
                 }
                 me.fireEvent('tabchange', me, newTab, oldTab);
             }
             return true;
        },

        /**
         * Makes the given group active
         * @param {Ext.Component} cmp The root component to make active.
         */
        setActiveGroup: function(cmp) {
            var me = this,
                newGroup = cmp,
                oldGroup;

            if(Ext.isString(cmp)) {
                newGroup = Ext.getCmp(newGroup);
            }

            if (newGroup === me.activeGroup) {
                return true;
            }

            oldGroup = me.activeGroup;
            if (me.fireEvent('beforegroupchange', me, newGroup, oldGroup) !== false) {
                 me.activeGroup = newGroup;
                 me.fireEvent('groupchange', me, newGroup, oldGroup);
             } else {
                 return false;
             }
             return true;
        },

        /**
         * @private
         * Creates the TreeStore used by the GroupTabBar.
         */
        createTreeStore: function() {
            var me = this,
                groups = me.prepareItems(me.items),
                data = {
                    text: '.',
                    children: []
                },
                cards = me.cards = [];
            me.activeGroup = me.activeGroup || 0;

            Ext.each(groups, function(groupItem, idx) {
                var leafItems = groupItem.items.items,
                    rootItem = (leafItems[groupItem.mainItem] || leafItems[0]),
                    groupRoot = {
                        children: []
                    };

                // Create the root node of the group
                groupRoot.id = rootItem.id;
                groupRoot.text = rootItem.title;
                groupRoot.iconCls = rootItem.iconCls;

                groupRoot.expanded = true;
                groupRoot.activeGroup = (me.activeGroup === idx);
                groupRoot.activeTab = groupRoot.activeGroup ? true : false;
                if (groupRoot.activeTab) {
                    me.activeTab = groupRoot.id;
                }

                if (groupRoot.activeGroup) {
                    me.mainItem = groupItem.mainItem || 0;
                    me.activeGroup = groupRoot.id;
                }

                Ext.each(leafItems, function(leafItem) {
                    // First node has been done
                    if (leafItem.id !== groupRoot.id) {
                        var child = {
                            id: leafItem.id,
                            leaf: true,
                            text: leafItem.title,
                            iconCls: leafItem.iconCls,
                            activeGroup: groupRoot.activeGroup,
                            activeTab: false
                        };
                        groupRoot.children.push(child);
                    }

                    // Ensure the items do not get headers
                    delete leafItem.title;
                    delete leafItem.iconCls;
                    cards.push(leafItem);
                });

                data.children.push(groupRoot);
          });

           return Ext.create('Ext.data.TreeStore', {
                fields: ['id', 'text', 'activeGroup', 'activeTab'],
                root: {
                    expanded: true
                },
                proxy: {
                    type: 'memory',
                    data: data
                }
            });
        },

        /**
         * Returns the item that is currently active inside this GroupTabPanel.
         * @return {Ext.Component/Number} The currently active item
         */
        getActiveTab: function() {
            return this.activeTab;
        },

        /**
         * Returns the root group item that is currently active inside this GroupTabPanel.
         * @return {Ext.Component/Number} The currently active root group item
         */
        getActiveGroup: function() {
            return this.activeGroup;
        }
    });