1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010-2021 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>
6 // Copyright (C) 2013-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "nel/gui/dbgroup_combo_box.h"
24 #include "nel/gui/group_menu.h"
25 #include "nel/misc/xml_auto_ptr.h"
26 #include "nel/gui/ctrl_button.h"
27 #include "nel/gui/action_handler.h"
28 #include "nel/gui/lua_ihm.h"
29 #include "nel/gui/widget_manager.h"
30 #include "nel/misc/i18n.h"
33 using namespace NLMISC
;
41 NLMISC_REGISTER_OBJECT(CViewBase
, CDBGroupComboBox
, std::string
, "combo_box");
43 void force_link_dbgroup_combo_box_cpp() { }
46 static inline bool lt_text(const std::pair
<int,std::string
> &s1
, const std::pair
<int,std::string
> &s2
)
48 // return toLower(s1.second) < toLower(s2.second);
49 return NLMISC::compareCaseInsensitive(s1
.second
, s2
.second
) < 0;
52 std::string
CDBGroupComboBox::measureMenu
;
53 std::string
CDBGroupComboBox::selectMenu
;
54 std::string
CDBGroupComboBox::selectMenuOut
;
56 // ***************************************************************************
57 CDBGroupComboBox::CDBGroupComboBox(const TCtorParam
¶m
)
58 : CInterfaceGroup(param
)
64 _NotLinkedToDBSelection
= 0;
65 _CallingOnChangeActionHandler
= false;
66 _IsExternViewText
= false;
70 // ***************************************************************************
71 CDBGroupComboBox::~CDBGroupComboBox()
75 // ***************************************************************************
76 sint32
CDBGroupComboBox::evalContentWidth() const
78 // get the menu to open.
79 CGroupMenu
*groupMenu
= dynamic_cast<CGroupMenu
*>(CWidgetManager::getInstance()->getElementFromId( CDBGroupComboBox::measureMenu
));
85 groupMenu
->setActive(true);
86 groupMenu
->updateCoords();
87 sint32 width
= groupMenu
->getWReal();
88 groupMenu
->setActive(false);
90 // add width for the selection arrow
94 std::string
CDBGroupComboBox::getProperty( const std::string
&name
) const
96 if( name
== "linked_to_db" )
98 return toString( _LinkedToDB
);
101 if( name
== "value" )
103 if( _Selection
.getNodePtr() != NULL
)
104 return _Selection
.getNodePtr()->getFullName();
109 return CInterfaceGroup::getProperty( name
);
112 void CDBGroupComboBox::setProperty( const std::string
&name
, const std::string
&value
)
114 if( name
== "linked_to_db" )
117 if( fromString( value
, b
) )
122 if( name
== "value" )
124 _Selection
.link( value
.c_str() );
128 CInterfaceGroup::setProperty( name
, value
);
131 xmlNodePtr
CDBGroupComboBox::serialize( xmlNodePtr parentNode
, const char *type
) const
133 xmlNodePtr node
= CInterfaceGroup::serialize( parentNode
, type
);
137 xmlSetProp( node
, BAD_CAST
"type", BAD_CAST
"combo_box" );
138 xmlSetProp( node
, BAD_CAST
"linked_to_db", BAD_CAST
toString( _LinkedToDB
).c_str() );
140 if( _Selection
.getNodePtr() != NULL
)
141 xmlSetProp( node
, BAD_CAST
"value", BAD_CAST _Selection
.getNodePtr()->getFullName().c_str() );
143 xmlSetProp( node
, BAD_CAST
"value", BAD_CAST
"" );
149 // ***************************************************************************
150 bool CDBGroupComboBox::parse (xmlNodePtr cur
, CInterfaceGroup
*parentGroup
)
152 if( !CInterfaceGroup::parse(cur
, parentGroup
) )
155 CXMLAutoPtr
prop((const char*)xmlGetProp(cur
, (xmlChar
*)"linked_to_db"));
158 _LinkedToDB
= convertBool(prop
);
163 // read the selection value
164 CXMLAutoPtr
prop((const char*)xmlGetProp(cur
, (xmlChar
*)"value"));
167 nlwarning("'value' not found in %s", _Id
.c_str());
172 _Selection
.link((const char*)prop
);
176 // read the sons text
178 child
= cur
->children
;
181 if (stricmp((char*)child
->name
,"combo_text") == 0)
183 CXMLAutoPtr
name((const char*) xmlGetProp (child
, (xmlChar
*)"name"));
186 const char *propPtr
= name
;
187 if (NLMISC::startsWith(propPtr
, "ui"))
188 addText(CI18N::get(propPtr
));
196 // optional ActionHandler on click
197 prop
= xmlGetProp(cur
, (xmlChar
*)"on_select_start");
198 if(prop
) _AHOnSelectStart
= (const char*)prop
;
200 // optional ActionHandler on change
201 prop
= xmlGetProp(cur
, (xmlChar
*)"on_change");
202 if(prop
) _AHOnChange
= (const char*)prop
;
204 prop
= xmlGetProp(cur
, (xmlChar
*)"on_change_params");
205 if(prop
) _AHOnChangeParams
= (const char*)prop
;
211 // ***************************************************************************
212 void CDBGroupComboBox::checkCoords ()
216 bool mustUpdate
= false;
219 // if some change in texts/selection
220 if(_DirtySelection
|| _CacheSelection
!=_Selection
.getSInt32())
222 _CacheSelection
= _Selection
.getSInt32();
228 // if some change in texts/selection
229 if(_DirtySelection
|| _CacheSelection
!= _NotLinkedToDBSelection
)
231 _CacheSelection
= _NotLinkedToDBSelection
;
237 // change selected text
238 if(_CacheSelection
<0 || _CacheSelection
>=(sint32
)_Texts
.size() )
240 _ViewText
->setText(std::string());
242 else if(_IsExternViewText
)
244 _ViewText
->setText(_ExternViewText
);
248 _ViewText
->setText(_Texts
[_CacheSelection
].second
);
253 // ***************************************************************************
254 void CDBGroupComboBox::updateCoords ()
258 CInterfaceGroup::updateCoords();
262 // ***************************************************************************
263 void CDBGroupComboBox::dirt()
265 _DirtySelection
= true;
268 // ***************************************************************************
269 void CDBGroupComboBox::resetTexts()
277 // ***************************************************************************
278 void CDBGroupComboBox::addText(const std::string
&text
)
281 _Texts
.push_back(make_pair((uint
)_Texts
.size(), text
));
282 _Textures
.push_back(std::string());
283 _Grayed
.push_back(false);
286 // ***************************************************************************
287 void CDBGroupComboBox::setText(uint i
, const std::string
&text
)
291 _Texts
[i
].second
= text
;
294 // ***************************************************************************
295 void CDBGroupComboBox::insertText(uint i
, const std::string
&text
)
300 addText(_Texts
[_Texts
.size()-1].second
);
302 for(uint t
=i
; t
<_Texts
.size()-1; t
++)
304 _Texts
[t
+1] = _Texts
[t
];
305 _Textures
[t
+1] = _Textures
[t
];
306 _Grayed
[t
+1] = _Grayed
[t
];
308 _Texts
[i
] = make_pair(i
, text
);
309 _Textures
[i
] = std::string();
312 else if(i
==_Texts
.size())
316 // ***************************************************************************
317 void CDBGroupComboBox::setTexture(uint i
, const std::string
&texture
)
320 if(i
<_Textures
.size())
321 _Textures
[i
]= texture
;
324 // ***************************************************************************
325 void CDBGroupComboBox::setGrayed(uint i
, bool g
)
332 // ***************************************************************************
333 bool CDBGroupComboBox::getGrayed(uint i
) const
342 // ***************************************************************************
343 void CDBGroupComboBox::removeText(uint nPos
)
346 if(nPos
<_Texts
.size())
348 _Texts
.erase( _Texts
.begin()+nPos
);
349 _Textures
.erase( _Textures
.begin()+nPos
);
350 _Grayed
.erase ( _Grayed
.begin()+nPos
);
354 // ***************************************************************************
355 const std::string
&CDBGroupComboBox::getText(uint i
) const
357 static const std::string empty
;
358 if (i
< _Texts
.size())
359 return _Texts
[i
].second
;
364 #ifdef RYZOM_LUA_UCSTRING
365 // ***************************************************************************
366 ucstring
CDBGroupComboBox::getTextAsUtf16(uint i
) const
368 return ucstring::makeFromUtf8(getText(i
));
372 // ***************************************************************************
373 uint
CDBGroupComboBox::getTextId(uint i
) const
375 static uint null
= 0;
377 return _Texts
[i
].first
;
382 // ***************************************************************************
383 uint
CDBGroupComboBox::getTextPos(uint nId
) const
385 for(uint i
=0; i
<_Texts
.size(); i
++)
387 if(nId
== _Texts
[i
].first
) {return i
;}
392 // ***************************************************************************
393 void CDBGroupComboBox::sortText()
395 sort(_Texts
.begin(), _Texts
.end(), lt_text
);
398 // ***************************************************************************
399 const std::string
&CDBGroupComboBox::getTexture(uint i
) const
401 static const std::string empty
;
402 if (i
< _Textures
.size())
408 #ifdef RYZOM_LUA_UCSTRING
409 // ***************************************************************************
410 ucstring
CDBGroupComboBox::getTextureAsUtf16(uint i
) const
412 return ucstring::makeFromUtf8(getTexture(i
));
416 // ***************************************************************************
417 void CDBGroupComboBox::setSelection(sint32 val
)
419 _IsExternViewText
= false;
423 _Selection
.setSInt32(val
);
427 _NotLinkedToDBSelection
= val
;
429 if (!_AHOnChange
.empty())
431 if (!_CallingOnChangeActionHandler
)
433 _CallingOnChangeActionHandler
= true;
434 CAHManager::getInstance()->runActionHandler (_AHOnChange
, this, _AHOnChangeParams
);
435 _CallingOnChangeActionHandler
= false;
440 // ***************************************************************************
441 void CDBGroupComboBox::setSelectionNoTrigger(sint32 val
)
445 _Selection
.setSInt32(val
);
449 _NotLinkedToDBSelection
= val
;
453 // ***************************************************************************
454 sint32
CDBGroupComboBox::getSelection() const
458 return _Selection
.getSInt32();
462 return _NotLinkedToDBSelection
;
466 // ***************************************************************************
467 void CDBGroupComboBox::setSelectionText(const std::string
& val
)
470 for(uint i
=0; i
<getNumTexts(); i
++)
472 std::string sText
= getText(i
);
482 // ***************************************************************************
483 void CDBGroupComboBox::setOnChange(const std::string
& val
)
486 _AHOnChangeParams
= val
;
489 // ***************************************************************************
490 void CDBGroupComboBox::setViewText(const std::string
&text
)
492 _IsExternViewText
= true;
493 _ExternViewText
= text
;
494 _ViewText
->setText(_ExternViewText
);
497 // ***************************************************************************
498 std::string
CDBGroupComboBox::getViewText() const
500 return _ViewText
->getText();
503 #ifdef RYZOM_LUA_UCSTRING
504 // ***************************************************************************
505 ucstring
CDBGroupComboBox::getViewTextAsUtf16() const
507 return CUtfStringView(_ViewText
->getText()).toUtf16();
511 // ***************************************************************************
512 CViewText
*CDBGroupComboBox::getViewText()
517 // ***************************************************************************
518 std::string
CDBGroupComboBox::getSelectionText() const
522 return getText(_Selection
.getSInt32());
526 return getText(_NotLinkedToDBSelection
);
530 // ***************************************************************************
531 std::string
CDBGroupComboBox::getOnChange() const
533 return _AHOnChangeParams
;
537 // ***************************************************************************
538 void CDBGroupComboBox::setup()
543 _ViewText
= dynamic_cast<CViewText
*>(CInterfaceGroup::getView("text"));
545 _SelectButton
= dynamic_cast<CCtrlBaseButton
*>(CInterfaceGroup::getCtrl("select"));
547 // force the action handler
549 _SelectButton
->setActionOnLeftClick("combo_box_select_start");
554 // ***************************************************************************
555 int CDBGroupComboBox::luaRemoveSelection(CLuaState
&ls
)
557 CLuaIHM::checkArgCount(ls
, "removeSelection", 1);
559 if(CLuaIHM::popSINT32(ls
, value
))
566 // ***************************************************************************
567 int CDBGroupComboBox::luaRemoveText(CLuaState
&ls
)
569 CLuaIHM::checkArgCount(ls
, "removeText", 1);
570 CLuaIHM::checkArgType(ls
, "removeText", 1, LUA_TSTRING
);
571 std::string text
= ls
.toString(1);
573 for(uint i
=0; i
<getNumTexts(); i
++)
575 std::string sText
= getText(i
);
586 // ***************************************************************************
587 int CDBGroupComboBox::luaResetTexts(CLuaState
&ls
)
589 CLuaIHM::checkArgCount(ls
, "resetTexts", 0);
594 // ***************************************************************************
595 int CDBGroupComboBox::luaAddText(CLuaState
&ls
)
597 const char *funcName
= "addText";
598 CLuaIHM::checkArgCount(ls
, funcName
, 1);
599 #ifdef RYZOM_LUA_UCSTRING
600 CLuaIHM::checkArgTypeUCString(ls
, funcName
, 1);
601 ucstring text
; // Compatibility
602 nlverify(CLuaIHM::pop(ls
, text
));
603 addText(text
.toUtf8());
605 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TSTRING
);
606 string text
= ls
.toString(1);
612 // ***************************************************************************
613 int CDBGroupComboBox::luaSetText(CLuaState
&ls
)
615 const char *funcName
= "setText";
616 CLuaIHM::checkArgCount(ls
, funcName
, 2);
617 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
618 #ifdef RYZOM_LUA_UCSTRING
619 CLuaIHM::checkArgTypeUCString(ls
, funcName
, 2);
620 ucstring text
; // Compatibility
621 nlverify(CLuaIHM::pop(ls
, text
));
622 setText((uint
) ls
.toInteger(1), text
.toUtf8());
624 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TSTRING
);
625 string text
= ls
.toString(2);
626 setText((uint
)ls
.toInteger(1), text
);
631 // ***************************************************************************
632 int CDBGroupComboBox::luaInsertText(CLuaState
&ls
)
634 const char *funcName
= "insertText";
635 CLuaIHM::checkArgCount(ls
, funcName
, 2);
636 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
637 #ifdef RYZOM_LUA_UCSTRING
638 CLuaIHM::checkArgTypeUCString(ls
, funcName
, 2);
640 nlverify(CLuaIHM::pop(ls
, text
));
641 insertText((uint
) ls
.toInteger(1), text
.toUtf8()); // FIXME: Lua should just do UTF-8!
643 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TSTRING
);
644 string text
= ls
.toString(2);
645 insertText((uint
)ls
.toInteger(1), text
);
650 // ***************************************************************************
651 int CDBGroupComboBox::luaSetTexture(CLuaState
&ls
)
653 const char *funcName
= "setTexture";
654 CLuaIHM::checkArgCount(ls
, funcName
, 2);
655 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
656 #ifdef RYZOM_LUA_UCSTRING
657 CLuaIHM::checkArgTypeUCString(ls
, funcName
, 2);
659 nlverify(CLuaIHM::pop(ls
, texture
));
660 setTexture((uint
) ls
.toInteger(1), texture
.toUtf8()); // FIXME: Lua should just do UTF-8!
662 CLuaIHM::checkArgType(ls
, funcName
, 2, LUA_TSTRING
);
663 string texture
= ls
.toString(2);
664 setTexture((uint
)ls
.toInteger(1), texture
);
669 // ***************************************************************************
670 int CDBGroupComboBox::luaGetText(CLuaState
&ls
)
672 const char *funcName
= "getText";
673 CLuaIHM::checkArgCount(ls
, funcName
, 1);
674 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
675 #ifdef RYZOM_LUA_UCSTRING
676 CLuaIHM::push(ls
, getTextAsUtf16((uint
) ls
.toInteger(1)));
678 ls
.push(getText((uint
)ls
.toInteger(1)));
683 // ***************************************************************************
684 int CDBGroupComboBox::luaRemoveTextByIndex(CLuaState
&ls
)
686 const char *funcName
= "removeText";
687 CLuaIHM::checkArgCount(ls
, funcName
, 1);
688 CLuaIHM::checkArgType(ls
, funcName
, 1, LUA_TNUMBER
);
689 removeText((uint
)ls
.toInteger(1));
693 // ***************************************************************************
694 int CDBGroupComboBox::luaGetNumTexts(CLuaState
&ls
)
696 const char *funcName
= "getNumTexts";
697 CLuaIHM::checkArgCount(ls
, funcName
, 0);
698 ls
.push(getNumTexts());
702 // ***************************************************************************
703 void CDBGroupComboBox::fillMenu(CGroupMenu
*groupMenu
) const
708 groupMenu
->setFontSize(_ViewText
->getFontSize());
710 // Setup the menu with combo action.
712 for(uint i
=0; i
<getNumTexts(); i
++)
714 // set the id as the parameter
715 bool checkable
= false;
716 if (getTexture(i
) != std::string())
720 groupMenu
->addLine(getText(i
), "combo_box_select_end", toString(i
),
721 "", std::string(), getTexture(i
), checkable
);
722 groupMenu
->setGrayedLine(i
, getGrayed(i
));
730 // ***************************************************************************
731 class CHandlerComboBoxSelectStart
: public IActionHandler
734 virtual void execute (CCtrlBase
*pCaller
, const std::string
&/* Params */)
736 CDBGroupComboBox
*pCB
= dynamic_cast<CDBGroupComboBox
*>(pCaller
->getParent());
737 if (pCB
== NULL
) return;
738 // if no choice, return.
739 if( pCB
->getNumTexts()==0 )
742 // get the menu to open.
743 CGroupMenu
*groupMenu
= dynamic_cast<CGroupMenu
*>(CWidgetManager::getInstance()->getElementFromId( CDBGroupComboBox::selectMenu
));
746 groupMenu
= dynamic_cast<CGroupMenu
*>(CWidgetManager::getInstance()->getElementFromId( CDBGroupComboBox::selectMenuOut
));
751 pCB
->fillMenu(groupMenu
);
753 groupMenu
->setMaxVisibleLine(8);
755 // pos and size wisely the menu.
756 groupMenu
->setMinW(pCB
->getWReal());
757 groupMenu
->setX(pCB
->getXReal());
758 groupMenu
->setBaseX(pCB
->getXReal());
759 groupMenu
->setY(pCB
->getYReal());
760 groupMenu
->setBaseY(pCB
->getYReal());
762 // Must ensure the combo menu has same windows priority than the combo box window
763 CInterfaceGroup
*rootWin
= pCB
->getRootWindow();
765 groupMenu
->setPriority(rootWin
->getPriority());
767 // After menu init, Call user activation method
768 if( !pCB
->_AHOnSelectStart
.empty() )
770 CAHManager::getInstance()->runActionHandler(pCB
->_AHOnSelectStart
, pCB
);
774 // if the combo box is in a modal, must do a push, else just replace
775 if(dynamic_cast<CGroupModal
*>(pCB
->getRootWindow()))
777 groupMenu
->setCloseSubMenuUsingPopModal(true);
778 CWidgetManager::getInstance()->pushModalWindow(pCB
, groupMenu
);
782 groupMenu
->setCloseSubMenuUsingPopModal(false);
783 CWidgetManager::getInstance()->enableModalWindow (pCB
, groupMenu
);
787 REGISTER_ACTION_HANDLER (CHandlerComboBoxSelectStart
, "combo_box_select_start");
792 // ***************************************************************************
793 class CHandlerComboBoxSelectEnd
: public IActionHandler
796 virtual void execute (CCtrlBase
* /* pCaller */, const std::string
&Params
)
798 CDBGroupComboBox
*pCB
= dynamic_cast<CDBGroupComboBox
*>(CWidgetManager::getInstance()->getCtrlLaunchingModal());
799 if (pCB
== NULL
) return;
803 fromString(Params
, selection
);
804 pCB
->setSelection(selection
);
807 REGISTER_ACTION_HANDLER (CHandlerComboBoxSelectEnd
, "combo_box_select_end");