1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "nel/gui/group_tab.h"
23 #include "nel/misc/xml_auto_ptr.h"
24 #include "nel/gui/lua_ihm.h"
25 #include "nel/gui/widget_manager.h"
26 #include "nel/gui/interface_group.h"
27 #include "nel/gui/view_text.h"
30 using namespace NLMISC
;
36 NLMISC_REGISTER_OBJECT(CViewBase
, CGroupTab
, std::string
, "tab");
41 // ***************************************************************************
42 CGroupTab::CGroupTab(const TCtorParam
¶m
)
43 : CInterfaceGroup(param
)
54 std::string
CGroupTab::getProperty( const std::string
&name
) const
56 if( name
== "hide_out_tabs" )
58 return toString( _HideOutTabs
);
61 if( name
== "onchange" )
66 if( name
== "onchange_params" )
68 return _ParamsOnChange
;
71 return CInterfaceGroup::getProperty( name
);
75 void CGroupTab::setProperty( const std::string
&name
, const std::string
&value
)
77 if( name
== "hide_out_tabs" )
80 if( fromString( value
, b
) )
85 if( name
== "onchange" )
91 if( name
== "onchange_params" )
93 _ParamsOnChange
= value
;
97 CInterfaceGroup::setProperty( name
, value
);
100 xmlNodePtr
CGroupTab::serialize( xmlNodePtr parentNode
, const char *type
) const
102 xmlNodePtr node
= CInterfaceGroup::serialize( parentNode
, type
);
106 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"tab" );
107 xmlSetProp( node
, BAD_CAST
"hide_out_tabs", BAD_CAST
toString( _HideOutTabs
).c_str() );
108 xmlSetProp( node
, BAD_CAST
"onchange", BAD_CAST _AHOnChange
.c_str() );
109 xmlSetProp( node
, BAD_CAST
"onchange_params", BAD_CAST _ParamsOnChange
.c_str() );
114 // ***************************************************************************
115 bool CGroupTab::parse (xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
117 if( !CInterfaceGroup::parse(cur
, parentGroup
) )
120 CXMLAutoPtr
prop((const char*)xmlGetProp(cur
, (xmlChar
*)"hide_out_tabs"));
123 _HideOutTabs
= convertBool(prop
);
126 prop
= (char*) xmlGetProp( cur
, (xmlChar
*)"onchange" );
127 if (prop
) _AHOnChange
= (const char *) prop
;
128 prop
= (char*) xmlGetProp( cur
, (xmlChar
*)"onchange_params" );
129 if (prop
) _ParamsOnChange
= (const char *) prop
;
134 // ***************************************************************************
135 void CGroupTab::setup()
144 /* Buttons must be named tab0,tab1,tab2...
145 and tab_array0_0, tab_array0_1 .... (for vector of tab)
146 Only 10 tab array are allowed
148 for(sint tabArrayIndex
= -1;tabArrayIndex
<10;tabArrayIndex
++)
150 // prefix according to array or not
152 if(tabArrayIndex
==-1)
155 prefix
= toString("tab_array%d_", tabArrayIndex
);
157 // for all tab of this type (standard tab or array of tab), find the Buttons and groups.
161 // find the ctrl named "tab0"
162 CCtrlTabButton
*but
= dynamic_cast<CCtrlTabButton
*>(getCtrl(toString("%s%d", prefix
.c_str(), tabIndex
)));
166 // find the associated group
167 CInterfaceGroup
*pGroup
= NULL
;
168 CInterfaceGroup
*pFather
= this;
170 while ((pGroup
== NULL
) && (pFather
!= NULL
))
172 pGroup
= pFather
->getGroup(but
->_AssociatedGroup
);
173 pFather
= pFather
->getParent();
176 // add to the button and group list
177 _Buttons
.push_back(but
);
178 _Groups
.push_back(pGroup
);
185 // at the first setup, select by default the 1st
190 // ***************************************************************************
191 void CGroupTab::addTab(CCtrlTabButton
* tabB
)
196 selectFromCtrl(tabB
);
198 if(_HideOutTabs
&& !_AHOnChange
.empty())
199 CAHManager::getInstance()->runActionHandler(_AHOnChange
, this, _ParamsOnChange
);
203 // ***************************************************************************
204 void CGroupTab::addTab(CCtrlTabButton
* tabB
, sint index
)
206 if(index
<(sint
)_Buttons
.size() && index
>=0)
208 vector
<CCtrlTabButton
*> buttons
= _Buttons
;
210 for(sint i
=0;i
<(sint
)_Buttons
.size();i
++)
211 delCtrl(_Buttons
[i
], true);
217 CCtrlTabButton
* lastTab
=NULL
;
218 for(sint i
=0;i
<(sint
)buttons
.size();i
++)
222 tabB
->setId("tab" + NLMISC::toString(count
));
223 tabB
->setParentPos(lastTab
);
225 tabB
->setParentPosRef(Hotspot_TL
);
227 tabB
->setParentPosRef(Hotspot_TR
);
228 tabB
->setPosRef(Hotspot_TL
);
235 buttons
[i
]->setId("tab" + NLMISC::toString(count
));
236 buttons
[i
]->setParentPos(lastTab
);
238 buttons
[i
]->setParentPosRef(Hotspot_TL
);
240 buttons
[i
]->setParentPosRef(Hotspot_TR
);
241 buttons
[i
]->setPosRef(Hotspot_TL
);
245 lastTab
= buttons
[i
];
252 // we have added a new button in first position
253 // then it must recover the reference
256 CCtrlTabButton
* tab0
= _Buttons
[0];
257 for(uint i
=0; i
<_Buttons
.size(); ++i
)
258 _Buttons
[i
]->initRBRefFromRadioButton(tab0
);
264 CCtrlTabButton
* tab0
= _Buttons
[0];
265 _Buttons
[index
]->initRBRefFromRadioButton(tab0
);
270 tabB
->setId(string("tab") + NLMISC::toString(_Buttons
.size()));
274 tabB
->setParentPos(NULL
);
275 tabB
->setParentPosRef(Hotspot_TL
);
279 tabB
->setParentPos(_Buttons
[_Buttons
.size()-1]);
280 tabB
->setParentPosRef(Hotspot_TR
);
282 tabB
->setPosRef(Hotspot_TL
);
290 if(_HideOutTabs
&& !_AHOnChange
.empty())
291 CAHManager::getInstance()->runActionHandler(_AHOnChange
, this, _ParamsOnChange
);
295 // ***************************************************************************
296 int CGroupTab::luaAddTab(CLuaState
&ls
)
298 CLuaIHM::checkArgCount(ls
, "CGroupTab::addTab", 1);
299 CCtrlTabButton
*tabB
= dynamic_cast<CCtrlTabButton
*>(CLuaIHM::getUIOnStack(ls
, 1));
302 // don't use addTab to avoid selection of new tab
308 if(_HideOutTabs
&& !_AHOnChange
.empty())
309 CAHManager::getInstance()->runActionHandler(_AHOnChange
, this, _ParamsOnChange
);
315 // ***************************************************************************
316 int CGroupTab::luaAddTabWithOrder(CLuaState
&ls
)
318 const char *funcName
= "addTabWithOrder";
319 CLuaIHM::checkArgCount(ls
, funcName
, 2);
320 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TNUMBER
);
322 CCtrlTabButton
*tabB
= dynamic_cast<CCtrlTabButton
*>(CLuaIHM::getUIOnStack(ls
, 1));
325 // don't use addTab to avoid selection of new tab
326 addTab(tabB
, (sint
) ls
.toInteger(2));
331 // ***************************************************************************
332 void CGroupTab::removeTab(sint index
)
334 if(!(index
>=0 && index
<(sint
)_Buttons
.size()))
337 vector
<CCtrlTabButton
*> buttons
= _Buttons
;
339 for(sint i
=0;i
<(sint
)_Buttons
.size();i
++)
341 bool deleteElt
= (i
!=index
);
342 CViewText
* tabVT
= _Buttons
[i
]->getViewText();
343 if(tabVT
&& !deleteElt
)
344 delView(tabVT
, deleteElt
);
346 delCtrl(_Buttons
[i
], deleteElt
);
349 (_Groups
[i
]->getParent())->delGroup(_Groups
[i
], deleteElt
);
355 CCtrlTabButton
* lastTab
= NULL
;
356 for(sint i
=0;i
<(sint
)buttons
.size();i
++)
360 buttons
[i
]->setId("tab"+NLMISC::toString(count
));
361 buttons
[i
]->setParentPos(lastTab
);
362 if((i
==0) || (index
==0 && i
==1))
363 buttons
[i
]->setParentPosRef(Hotspot_TL
);
365 _Buttons
[i
]->setParentPosRef(Hotspot_TR
);
367 buttons
[i
]->setPosRef(Hotspot_TL
);
369 lastTab
= buttons
[i
];
378 // we have removed the first button which is the only one to own the reference
379 // then the new first button recovers the reference
382 CCtrlTabButton
* tab0
= _Buttons
[0];
383 for(uint i
=0; i
<_Buttons
.size(); ++i
)
384 _Buttons
[i
]->initRBRefFromRadioButton(tab0
);
391 select(_FirstTabIndex
);
393 if(!_AHOnChange
.empty())
394 CAHManager::getInstance()->runActionHandler(_AHOnChange
, this, _ParamsOnChange
);
398 // ***************************************************************************
400 int CGroupTab::luaRemoveTab(CLuaState
&ls
)
402 const char *funcName
= "removeTab";
403 CLuaIHM::checkArgCount(ls
, funcName
, 1);
404 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
405 removeTab((uint
) ls
.toInteger(1));
409 // ***************************************************************************
410 void CGroupTab::removeAll()
412 for(sint i
=0;i
<(sint
)_Buttons
.size();i
++)
414 CViewText
* tabVT
= _Buttons
[i
]->getViewText();
416 delView(tabVT
, false);
418 delCtrl(_Buttons
[i
], false);
419 (_Groups
[i
]->getParent())->delGroup(_Groups
[i
], false);
426 // ***************************************************************************
428 int CGroupTab::luaRemoveAll(CLuaState
&ls
)
430 CLuaIHM::checkArgCount(ls
, "CGroupTab::removeAll", 0);
435 // ***************************************************************************
436 CCtrlTabButton
* CGroupTab::getTabButton(sint index
)
438 if(index
>=0 && index
<(sint
)_Buttons
.size())
440 return _Buttons
[index
];
445 // ***************************************************************************
446 int CGroupTab::luaGetTabButton(CLuaState
&ls
)
448 const char *funcName
= "getTabButton";
449 CLuaIHM::checkArgCount(ls
, funcName
, 1);
450 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
451 CCtrlTabButton
* tab
= getTabButton((uint
) ls
.toInteger(1));
454 CLuaIHM::pushUIOnStack(ls
, tab
);
460 // ***************************************************************************
461 void CGroupTab::updateFirstTabButton()
463 if(!_HideOutTabs
|| (_Selection
<0) || _Buttons
.empty() || (_Parent
->getWReal()<0)
464 || _FirstTabIndex
>=(sint
)_Buttons
.size())
467 sint oldFirstTabIndex
= _FirstTabIndex
;
468 sint oldLastTabIndex
= _LastTabIndex
;
472 for(uint i
=0; i
<_Buttons
.size(); i
++)
474 CCtrlTabButton
* tab
= _Buttons
[i
];
483 sint selection
= _Selection
;
484 if(selection
>=(sint
)_Buttons
.size())
485 selection
= _FirstTabIndex
;
487 if(selection
< _FirstTabIndex
)
488 _FirstTabIndex
= selection
;
490 sint32 maxWidth
= _Parent
->getWReal();
491 sint32 buttonsWidth
= 0;
494 // desactive first tabs
495 for(uint i
=0; i
<(uint
)_FirstTabIndex
; i
++)
497 CCtrlTabButton
* tab
= _Buttons
[i
];
499 tab
->setActive(false);
503 // active tabs from _FirstTabIndex and search for last showed tab
504 for(uint i
=_FirstTabIndex
; i
<_Buttons
.size(); i
++)
506 CCtrlTabButton
* tab
= _Buttons
[i
];
507 sint32 tabWidth
= tab
->getWMax();
508 if(buttonsWidth
+tabWidth
<= maxWidth
)
510 buttonsWidth
+= tabWidth
;
511 if(!tab
->getActive())
512 tab
->setActive(true);
519 // check if selected tab is in showed tabs
520 if(_LastTabIndex
< selection
)
522 for(uint i
=_LastTabIndex
+1; i
<=(uint
)selection
; i
++)
524 CCtrlTabButton
* tab
= _Buttons
[i
];
525 buttonsWidth
+= tab
->getWMax();
526 if(!tab
->getActive())
527 tab
->setActive(true);
530 while(buttonsWidth
>maxWidth
)
532 CCtrlTabButton
* tab
= _Buttons
[_FirstTabIndex
];
533 buttonsWidth
-= tab
->getWMax();
536 tab
->setActive(false);
540 // add tabs before the "_FirstTabIndex" one if it remains place
541 while(buttonsWidth
<maxWidth
&& _FirstTabIndex
>0)
543 CCtrlTabButton
* tab
= _Buttons
[_FirstTabIndex
-1];
544 buttonsWidth
+= tab
->getWMax();
545 if(buttonsWidth
<=maxWidth
)
548 if(!tab
->getActive())
549 tab
->setActive(true);
553 // desactive last tabs
554 for(uint i
=_LastTabIndex
+1; i
<_Buttons
.size(); i
++)
556 CCtrlTabButton
* tab
= _Buttons
[i
];
558 tab
->setActive(false);
561 if(!_AHOnChange
.empty() && ((oldFirstTabIndex
!=_FirstTabIndex
) || (oldLastTabIndex
!=_LastTabIndex
)))
562 CAHManager::getInstance()->runActionHandler(_AHOnChange
, this, _ParamsOnChange
);
566 // ***************************************************************************
567 int CGroupTab::luaShowTabButton(CLuaState
&ls
)
569 const char *funcName
= "showTabButton";
570 CLuaIHM::checkArgCount(ls
, funcName
, 1);
571 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
572 sint showTab
= (sint
)ls
.toInteger(1);
574 if(showTab
>=0 && showTab
<(sint
)_Buttons
.size())
576 sint32 maxWidth
= _Parent
->getWReal();
577 sint32 buttonsWidth
= 0;
579 if(showTab
<_FirstTabIndex
)
581 _FirstTabIndex
= showTab
;
582 sint lastTabIndex
= _FirstTabIndex
;
583 for(uint i
=_FirstTabIndex
; i
<_Buttons
.size(); i
++)
585 CCtrlTabButton
* tab
= _Buttons
[i
];
586 sint32 tabWidth
= tab
->getWMax();
587 if(buttonsWidth
+tabWidth
<= maxWidth
)
589 buttonsWidth
+= tabWidth
;
590 if(!tab
->getActive())
591 tab
->setActive(true);
598 if(lastTabIndex
<_Selection
)
599 select(lastTabIndex
);
601 updateFirstTabButton();
603 else if(showTab
>_LastTabIndex
)
605 for(uint i
=_FirstTabIndex
; i
<=(uint
)showTab
; i
++)
606 buttonsWidth
+= _Buttons
[i
]->getWMax();
608 while(buttonsWidth
>maxWidth
)
610 buttonsWidth
-= _Buttons
[_FirstTabIndex
]->getWMax();
614 if(_Selection
<_FirstTabIndex
)
615 select(_FirstTabIndex
);
617 updateFirstTabButton();
624 // ***************************************************************************
625 void CGroupTab::updateCoords ()
630 // special for groupTab. Because the ctrl may overlap each other from left to right, they are inserted in reverse
631 // order. BUT, for correct TR/TL coord handling, must updtae in the reverse sens too!
634 CInterfaceGroup::doUpdateCoords();
636 // **** update in reverse order
639 vector
<CViewBase
*>::reverse_iterator ite
;
640 for (ite
= _EltOrder
.rbegin() ; ite
!= _EltOrder
.rend(); ite
++)
642 CViewBase
*pIE
= *ite
;
648 // **** complete with child resize
649 CInterfaceGroup::updateCoords();
651 updateFirstTabButton();
654 // ***************************************************************************
655 void CGroupTab::select(sint index
)
660 if(index
<(sint
)_Buttons
.size())
664 // validate this radio button.
666 _Buttons
[index
]->setPushed(true);
668 for(i
=0;i
<(sint
)_Buttons
.size();i
++)
669 _Buttons
[i
]->setPushed(false);
671 _NextSelection
= index
;
673 // set all render layer to their correct state
674 for(i
=0;i
<(sint
)_Buttons
.size();i
++)
676 // set the selected one +1, so it will be over
677 _Buttons
[i
]->setRenderLayer(_BaseRenderLayer
+ (i
==index
?1:0) );
680 _Buttons
[i
]->setBlink(false);
681 if (_Buttons
[i
]->_AHOnLeftClick2
!= NULL
)
682 // call like if press on it
683 _Buttons
[i
]->_AHOnLeftClick2
->execute(_Buttons
[i
], _Buttons
[i
]->getParamsOnLeftClick());
687 // show/hide all the associated groups
688 for(i
=0;i
<(sint
)_Groups
.size();i
++)
691 _Groups
[i
]->setActive(i
==index
);
697 updateFirstTabButton();
701 // ***************************************************************************
702 void CGroupTab::selectFromCtrl(CCtrlTabButton
*button
)
704 // search in all buttons
705 for(uint i
=0;i
<_Buttons
.size();i
++)
708 if(_Buttons
[i
]==button
)
716 // ***************************************************************************
717 void CGroupTab::selectDefault(CCtrlTabButton
*ifSelectionIs
)
719 if(!_HideOutTabs
&& _Selection
>=0 && _Selection
<(sint
)_Buttons
.size() && _Buttons
[_Selection
]==ifSelectionIs
)
721 // parse all active button
722 for(uint i
=0;i
<_Buttons
.size();i
++)
724 if(_Buttons
[i
]->getActive())
736 // ***************************************************************************
737 void CGroupTab::selectDefaultIfCurrentHid()
739 if(_Selection
>=0 && _Selection
<(sint
)_Buttons
.size() &&
740 _Buttons
[_Selection
]!=NULL
&& _Buttons
[_Selection
]->getActive()==false)
742 selectDefault(_Buttons
[_Selection
]);
746 // ***************************************************************************
747 sint
CGroupTab::getSelection() const
752 NLMISC_REGISTER_OBJECT(CViewBase
, CCtrlTabButton
, std::string
, "tab_button");
754 // ***************************************************************************
755 std::string
CGroupTab::getAssociatedGroupSelection() const
757 if(_Selection
>=0 && _Selection
<(sint
)_Buttons
.size())
759 return _Buttons
[_Selection
]->_AssociatedGroup
;
764 // ***************************************************************************
765 CInterfaceGroup
* CGroupTab::getGroup(sint index
)
767 if(index
>=0 && index
<(sint
)_Groups
.size())
769 return _Groups
[index
];
774 // ***************************************************************************
775 int CGroupTab::luaGetGroup(CLuaState
&ls
)
777 const char *funcName
= "getGroup";
778 CLuaIHM::checkArgCount(ls
, funcName
, 1);
779 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
780 CInterfaceGroup
* group
= getGroup((uint
) ls
.toInteger(1));
783 CLuaIHM::pushUIOnStack(ls
, group
);
789 // ***************************************************************************
790 CCtrlTabButton::CCtrlTabButton(const TCtorParam
¶m
)
791 : CCtrlTextButton(param
)
794 _AHOnLeftClick2
= NULL
;
800 std::string
CCtrlTabButton::getProperty( const std::string
&name
) const
802 if( name
== "group" )
803 return _AssociatedGroup
;
805 return CCtrlTextButton::getProperty( name
);
808 void CCtrlTabButton::setProperty( const std::string
&name
, const std::string
&value
)
810 if( name
== "group" )
812 _AssociatedGroup
= value
;
815 CCtrlTextButton::setProperty( name
, value
);
818 xmlNodePtr
CCtrlTabButton::serialize( xmlNodePtr parentNode
, const char *type
) const
820 xmlNodePtr node
= CCtrlTextButton::serialize( parentNode
, type
);
824 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"tab" );
825 xmlNewProp( node
, BAD_CAST
"group", BAD_CAST _AssociatedGroup
.c_str() );
830 // ***************************************************************************
831 bool CCtrlTabButton::parse (xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
833 if(!CCtrlTextButton::parse(cur
, parentGroup
))
836 // if left click not setuped, set default
837 _AHOnLeftClick2
= _AHOnLeftClick
;
839 _AHOnLeftClick
= CAHManager::getInstance()->getAH("tab_select", dummy
);
841 // read the associated group to show/hide
843 prop
= (char*) xmlGetProp( cur
, (xmlChar
*)"group" );
844 if(prop
) _AssociatedGroup
= (const char*)prop
;
852 // ***************************************************************************
853 void CCtrlTabButton::setActive(bool state
)
855 if(state
!=getActive())
857 CCtrlTextButton::setActive(state
);
859 // special for correct display of textbuttons. reset to 0 when the button is hid
865 // if hide, and I was the selected tab, select a default active one
868 CGroupTab
*parent
= dynamic_cast<CGroupTab
*>(getParent());
870 parent
->selectDefault(this);
875 // ***************************************************************************
876 bool CCtrlTabButton::handleEvent (const NLGUI::CEventDescriptor
&event
)
878 if (event
.getType() == NLGUI::CEventDescriptor::system
)
880 const NLGUI::CEventDescriptorSystem
&systemEvent
= (const NLGUI::CEventDescriptorSystem
&) event
;
881 if (systemEvent
.getEventTypeExtended() == NLGUI::CEventDescriptorSystem::clocktick
)
884 uint dbclickDelay
= CWidgetManager::getInstance()->getUserDblClickDelay();
885 const CWidgetManager::SInterfaceTimes
×
= CWidgetManager::getInstance()->getInterfaceTimes();
887 if (( times
.thisFrameMs
- _BlinkDate
) > dbclickDelay
)
891 setTextColorNormal(CRGBA::White
);
892 setTextModulateGlobalColorNormal(false);
896 setTextColorNormal(_TextColorNormalBlink
);
897 setTextModulateGlobalColorNormal(_TextModulateGlobalColorNormalBlink
);
899 _BlinkState
= !_BlinkState
;
900 _BlinkDate
= times
.thisFrameMs
;
904 return CCtrlTextButton::handleEvent(event
);
907 // ***************************************************************************
908 void CCtrlTabButton::setBlink (bool b
)
914 _TextColorNormalBlink
= getTextColorNormal();
915 _TextModulateGlobalColorNormalBlink
= getTextModulateGlobalColorNormal();
916 CWidgetManager::getInstance()->registerClockMsgTarget(this);
924 CWidgetManager::getInstance()->unregisterClockMsgTarget(this);
925 setTextColorNormal(_TextColorNormalBlink
);
926 setTextModulateGlobalColorNormal(_TextModulateGlobalColorNormalBlink
);
932 // ***************************************************************************
933 // Action handler for Tab selection
934 class CHandlerTabSelect
: public IActionHandler
937 virtual void execute (CCtrlBase
*pCaller
, const string
&/* Params */)
939 CCtrlTabButton
*but
= dynamic_cast<CCtrlTabButton
*>(pCaller
);
941 // get the parent TabGroup
942 CGroupTab
*parent
= dynamic_cast<CGroupTab
*>(but
->getParent());
944 parent
->selectFromCtrl(but
);
947 REGISTER_ACTION_HANDLER(CHandlerTabSelect
, "tab_select" );