lluictrlfactory.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. /**
  2. * @file lluictrlfactory.cpp
  3. * @brief Factory class for creating UI controls
  4. *
  5. * $LicenseInfo:firstyear=2003&license=viewergpl$
  6. *
  7. * Copyright (c) 2003-2009, Linden Research, Inc.
  8. *
  9. * Second Life Viewer Source Code
  10. * The source code in this file ("Source Code") is provided by Linden Lab
  11. * to you under the terms of the GNU General Public License, version 2.0
  12. * ("GPL"), unless you have obtained a separate licensing agreement
  13. * ("Other License"), formally executed by you and Linden Lab. Terms of
  14. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  15. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  16. *
  17. * There are special exceptions to the terms and conditions of the GPL as
  18. * it is applied to this Source Code. View the full text of the exception
  19. * in the file doc/FLOSS-exception.txt in this software distribution, or
  20. * online at
  21. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  22. *
  23. * By copying, modifying or distributing this software, you acknowledge
  24. * that you have read and understood your obligations described above,
  25. * and agree to abide by those obligations.
  26. *
  27. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  28. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  29. * COMPLETENESS OR PERFORMANCE.
  30. * $/LicenseInfo$
  31. */
  32. #include "linden_common.h"
  33. #include <fstream>
  34. #include "boost/tokenizer.hpp"
  35. #include "lluictrlfactory.h"
  36. #include "llcolor4.h"
  37. #include "llcontrol.h"
  38. #include "lldir.h"
  39. #include "llmenugl.h"
  40. const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n";
  41. std::vector<std::string> LLUICtrlFactory::sXUIPaths;
  42. static const std::string LL_UI_CTRL_LOCATE_TAG = "locate";
  43. static const std::string LL_PAD_TAG = "pad";
  44. // UI Ctrl class for padding
  45. class LLUICtrlLocate : public LLUICtrl
  46. {
  47. public:
  48. LLUICtrlLocate()
  49. : LLUICtrl("locate", LLRect(0, 0, 0, 0), false, NULL, NULL)
  50. {
  51. setTabStop(false);
  52. }
  53. virtual void draw()
  54. {
  55. }
  56. virtual LLXMLNodePtr getXML(bool save_children = true) const
  57. {
  58. LLXMLNodePtr node = LLUICtrl::getXML();
  59. node->setName(LL_UI_CTRL_LOCATE_TAG);
  60. return node;
  61. }
  62. static LLView* fromXML(LLXMLNodePtr node, LLView* parent,
  63. LLUICtrlFactory* factory)
  64. {
  65. std::string name = LL_PAD_TAG;
  66. node->getAttributeString("name", name);
  67. LLUICtrlLocate* new_ctrl = new LLUICtrlLocate();
  68. new_ctrl->setName(name);
  69. new_ctrl->initFromXML(node, parent);
  70. return new_ctrl;
  71. }
  72. };
  73. static LLRegisterWidget<LLUICtrlLocate> r29(LL_UI_CTRL_LOCATE_TAG);
  74. static LLRegisterWidget<LLUICtrlLocate> r30(LL_PAD_TAG);
  75. LLUICtrlFactory::LLUICtrlFactory()
  76. : mDummyPanel(NULL)
  77. {
  78. setupPaths();
  79. }
  80. LLUICtrlFactory::~LLUICtrlFactory()
  81. {
  82. delete mDummyPanel;
  83. mDummyPanel = NULL;
  84. }
  85. void LLUICtrlFactory::setupPaths()
  86. {
  87. std::string filename = gDirUtil.getFullPath(LL_PATH_SKINS, "paths.xml");
  88. LLXMLNodePtr root;
  89. bool success = LLXMLNode::parseFile(filename, root, NULL);
  90. sXUIPaths.clear();
  91. if (success)
  92. {
  93. LLXMLNodePtr path;
  94. for (path = root->getFirstChild(); path.notNull();
  95. path = path->getNextSibling())
  96. {
  97. LLUIString path_val_ui(path->getValue());
  98. std::string language = LLUI::getLanguage();
  99. path_val_ui.setArg("[LANGUAGE]", language);
  100. if (std::find(sXUIPaths.begin(), sXUIPaths.end(),
  101. path_val_ui.getString()) == sXUIPaths.end())
  102. {
  103. sXUIPaths.emplace_back(path_val_ui.getString());
  104. }
  105. }
  106. }
  107. else // Parsing failed
  108. {
  109. llwarns << "XUI::config file unable to open: " << filename << llendl;
  110. sXUIPaths.emplace_back("xui" LL_DIR_DELIM_STR "en-us");
  111. }
  112. }
  113. //static
  114. const std::vector<std::string>& LLUICtrlFactory::getXUIPaths()
  115. {
  116. return sXUIPaths;
  117. }
  118. bool LLUICtrlFactory::getLayeredXMLNode(const std::string& xui_filename,
  119. LLXMLNodePtr& root)
  120. {
  121. std::string full_filename = gDirUtil.findSkinnedFilename(sXUIPaths.front(),
  122. xui_filename);
  123. if (full_filename.empty())
  124. {
  125. // Try filename as passed in since sometimes we load an xml file from a
  126. // user-supplied path
  127. if (LLFile::exists(xui_filename))
  128. {
  129. full_filename = xui_filename;
  130. }
  131. else
  132. {
  133. llwarns << "Could not find UI description file: "
  134. << sXUIPaths.front() + "/" + xui_filename
  135. << llendl;
  136. return false;
  137. }
  138. }
  139. if (!LLXMLNode::parseFile(full_filename, root, NULL))
  140. {
  141. llwarns << "Problem reading UI description file: " << full_filename
  142. << llendl;
  143. return false;
  144. }
  145. LLXMLNodePtr upd_root;
  146. std::string layer_filename, node_name, upd_name;
  147. for (std::vector<std::string>::const_iterator it = sXUIPaths.begin(),
  148. end = sXUIPaths.end();
  149. it != end; ++it)
  150. {
  151. // Skip the first path to only consider overrides
  152. if (it == sXUIPaths.begin()) continue;
  153. layer_filename = gDirUtil.findSkinnedFilename(*it, xui_filename);
  154. if (layer_filename.empty())
  155. {
  156. // No localized version of this file, that's ok, keep looking
  157. continue;
  158. }
  159. if (!LLXMLNode::parseFile(layer_filename, upd_root, NULL))
  160. {
  161. llwarns << "Problem reading localized UI description file: "
  162. << *it + LL_DIR_DELIM_STR + xui_filename << llendl;
  163. return false;
  164. }
  165. upd_root->getAttributeString("name", upd_name);
  166. root->getAttributeString("name", node_name);
  167. if (upd_name == node_name)
  168. {
  169. LLXMLNode::updateNode(root, upd_root);
  170. }
  171. }
  172. return true;
  173. }
  174. bool LLUICtrlFactory::buildFloater(LLFloater* floaterp,
  175. const std::string& filename,
  176. const LLCallbackMap::map_t* factory_map,
  177. bool open)
  178. {
  179. LLXMLNodePtr root;
  180. if (!getLayeredXMLNode(filename, root))
  181. {
  182. return false;
  183. }
  184. // root must be called "floater"
  185. if (!(root->hasName("floater") || root->hasName("multi_floater")))
  186. {
  187. llwarns << "Root node should be named floater in: " << filename
  188. << llendl;
  189. return false;
  190. }
  191. if (factory_map)
  192. {
  193. mFactoryStack.push_front(factory_map);
  194. }
  195. floaterp->initFloaterXML(root, NULL, this, open);
  196. if (LLUI::sShowXUINames)
  197. {
  198. floaterp->setToolTip(filename);
  199. }
  200. if (factory_map)
  201. {
  202. mFactoryStack.pop_front();
  203. }
  204. LLHandle<LLFloater> handle = floaterp->getHandle();
  205. mBuiltFloaters[handle] = filename;
  206. return true;
  207. }
  208. S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename)
  209. {
  210. llofstream out(filename.c_str());
  211. if (!out.is_open())
  212. {
  213. llwarns << "Unable to open " << filename << " for output." << llendl;
  214. return 1;
  215. }
  216. out << XML_HEADER;
  217. LLXMLNodePtr xml_node = viewp->getXML();
  218. xml_node->writeToOstream(out);
  219. out.close();
  220. return 0;
  221. }
  222. bool LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename,
  223. const LLCallbackMap::map_t* factory_map)
  224. {
  225. LLXMLNodePtr root;
  226. if (!getLayeredXMLNode(filename, root))
  227. {
  228. return false;
  229. }
  230. // root must be called "panel"
  231. if (!root->hasName("panel"))
  232. {
  233. llwarns << "Root node should be named panel in : " << filename
  234. << llendl;
  235. return false;
  236. }
  237. if (factory_map)
  238. {
  239. mFactoryStack.push_front(factory_map);
  240. }
  241. bool result = panelp->initPanelXML(root, NULL, this);
  242. if (LLUI::sShowXUINames)
  243. {
  244. panelp->setToolTip(filename);
  245. }
  246. LLHandle<LLPanel> handle = panelp->getHandle();
  247. mBuiltPanels[handle] = filename;
  248. if (factory_map)
  249. {
  250. mFactoryStack.pop_front();
  251. }
  252. return result;
  253. }
  254. LLMenuGL* LLUICtrlFactory::buildMenu(const std::string& filename,
  255. LLView* parentp)
  256. {
  257. LLXMLNodePtr root;
  258. LLMenuGL* menu;
  259. if (!getLayeredXMLNode(filename, root))
  260. {
  261. return NULL;
  262. }
  263. // root must be called "menu_bar" or "menu"
  264. if (!root->hasName("menu_bar") && !root->hasName("menu"))
  265. {
  266. llwarns << "Root node should be named menu bar or menu in: "
  267. << filename << llendl;
  268. return NULL;
  269. }
  270. if (root->hasName("menu"))
  271. {
  272. menu = (LLMenuGL*)LLMenuGL::fromXML(root, parentp, this);
  273. }
  274. else
  275. {
  276. menu = (LLMenuGL*)LLMenuBarGL::fromXML(root, parentp, this);
  277. }
  278. if (LLUI::sShowXUINames)
  279. {
  280. menu->setToolTip(filename);
  281. }
  282. return menu;
  283. }
  284. LLPieMenu* LLUICtrlFactory::buildPieMenu(const std::string& filename,
  285. LLView* parentp)
  286. {
  287. LLXMLNodePtr root;
  288. if (!getLayeredXMLNode(filename, root))
  289. {
  290. return NULL;
  291. }
  292. // root must be called "pie_menu"
  293. if (!root->hasName(LL_PIE_MENU_TAG))
  294. {
  295. llwarns << "Root node should be named " << LL_PIE_MENU_TAG << " in: "
  296. << filename << llendl;
  297. return NULL;
  298. }
  299. std::string name = "menu";
  300. root->getAttributeString("name", name);
  301. LLPieMenu* menu = new LLPieMenu(name);
  302. parentp->addChild(menu);
  303. menu->initXML(root, parentp, this);
  304. if (LLUI::sShowXUINames)
  305. {
  306. menu->setToolTip(filename);
  307. }
  308. return menu;
  309. }
  310. void LLUICtrlFactory::rebuild()
  311. {
  312. built_panel_t::iterator built_panel_it;
  313. for (built_panel_it = mBuiltPanels.begin();
  314. built_panel_it != mBuiltPanels.end(); ++built_panel_it)
  315. {
  316. std::string filename = built_panel_it->second;
  317. LLPanel* panelp = built_panel_it->first.get();
  318. if (!panelp)
  319. {
  320. continue;
  321. }
  322. llinfos << "Rebuilding UI panel " << panelp->getName() << " from "
  323. << filename << llendl;
  324. bool visible = panelp->getVisible();
  325. panelp->setVisible(false);
  326. panelp->setFocus(false);
  327. panelp->deleteAllChildren();
  328. buildPanel(panelp, filename.c_str(), &panelp->getFactoryMap());
  329. panelp->setVisible(visible);
  330. }
  331. built_floater_t::iterator built_floater_it;
  332. for (built_floater_it = mBuiltFloaters.begin();
  333. built_floater_it != mBuiltFloaters.end(); ++built_floater_it)
  334. {
  335. LLFloater* floaterp = built_floater_it->first.get();
  336. if (!floaterp)
  337. {
  338. continue;
  339. }
  340. std::string filename = built_floater_it->second;
  341. llinfos << "Rebuilding UI floater " << floaterp->getName() << " from "
  342. << filename << llendl;
  343. bool visible = floaterp->getVisible();
  344. floaterp->setVisible(false);
  345. floaterp->setFocus(false);
  346. floaterp->deleteAllChildren();
  347. gFloaterViewp->removeChild(floaterp);
  348. buildFloater(floaterp, filename, &floaterp->getFactoryMap());
  349. floaterp->setVisible(visible);
  350. }
  351. }
  352. LLView* LLUICtrlFactory::createCtrlWidget(LLPanel* parent, LLXMLNodePtr node)
  353. {
  354. std::string ctrl_type = node->getName()->mString;
  355. LLStringUtil::toLower(ctrl_type);
  356. LLWidgetClassRegistry::factory_func_t func =
  357. LLWidgetClassRegistry::getInstance()->getCreatorFunc(ctrl_type);
  358. if (!func)
  359. {
  360. llwarns << "Invalid control type '" << ctrl_type << "' - Parent: "
  361. << (parent ? parent->getName() : "none") << llendl;
  362. return NULL;
  363. }
  364. if (!parent)
  365. {
  366. if (!mDummyPanel)
  367. {
  368. mDummyPanel = new LLPanel;
  369. }
  370. parent = mDummyPanel;
  371. }
  372. LLView* ctrl = func(node, parent, this);
  373. return ctrl;
  374. }
  375. LLView* LLUICtrlFactory::createWidget(LLPanel* parent, LLXMLNodePtr node)
  376. {
  377. LLView* view = createCtrlWidget(parent, node);
  378. S32 tab_group = parent->getLastTabGroup();
  379. node->getAttributeS32("tab_group", tab_group);
  380. if (view)
  381. {
  382. parent->addChild(view, tab_group);
  383. }
  384. return view;
  385. }
  386. LLPanel* LLUICtrlFactory::createFactoryPanel(const std::string& name)
  387. {
  388. std::deque<const LLCallbackMap::map_t*>::iterator itor;
  389. for (itor = mFactoryStack.begin(); itor != mFactoryStack.end(); ++itor)
  390. {
  391. const LLCallbackMap::map_t* factory_map = *itor;
  392. // Look up this panel's name in the map.
  393. LLCallbackMap::map_const_iter_t iter = factory_map->find(name);
  394. if (iter != factory_map->end())
  395. {
  396. // Use the factory to create the panel, instead of using a default
  397. // LLPanel.
  398. LLPanel* ret = (LLPanel*)iter->second.mCallback(iter->second.mData);
  399. return ret;
  400. }
  401. }
  402. return NULL;
  403. }
  404. //static
  405. bool LLUICtrlFactory::getAttributeColor(LLXMLNodePtr node,
  406. const std::string& name,
  407. LLColor4& color)
  408. {
  409. std::string colorstring;
  410. bool res = node->getAttributeString(name.c_str(), colorstring);
  411. if (res && LLUI::sColorsGroup)
  412. {
  413. if (LLUI::sColorsGroup->controlExists(colorstring.c_str()))
  414. {
  415. color.set(LLUI::sColorsGroup->getColor(colorstring.c_str()));
  416. }
  417. else
  418. {
  419. res = false;
  420. }
  421. }
  422. if (!res)
  423. {
  424. res = LLColor4::parseColor(colorstring, &color);
  425. }
  426. if (!res)
  427. {
  428. res = node->getAttributeColor(name.c_str(), color);
  429. }
  430. return res;
  431. }