llnamelistctrl.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /**
  2. * @file llnamelistctrl.cpp
  3. * @brief A list of names, automatically refreshed from name cache.
  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 "boost/tokenizer.hpp"
  34. #include "llnamelistctrl.h"
  35. #include "llframetimer.h"
  36. #include "llinventory.h"
  37. static const std::string LL_NAME_LIST_CTRL_TAG = "name_list";
  38. static LLRegisterWidget<LLNameListCtrl> r13(LL_NAME_LIST_CTRL_TAG);
  39. //static
  40. LLNameListCtrl::instances_list_t LLNameListCtrl::sInstances;
  41. LLNameListCtrl::LLNameListCtrl(const std::string& name, const LLRect& rect,
  42. LLUICtrlCallback cb, void* userdata,
  43. bool allow_multiple_selection, bool draw_border,
  44. S32 name_column_index,
  45. const std::string& tooltip)
  46. : LLScrollListCtrl(name, rect, cb, userdata, allow_multiple_selection,
  47. draw_border),
  48. mNameColumnIndex(name_column_index),
  49. mAllowCallingCardDrop(false),
  50. mUseDisplayNames(false),
  51. mLastUpdate(0.0),
  52. mLazyUpdateInterval(0.f)
  53. {
  54. setToolTip(tooltip);
  55. sInstances.insert(this);
  56. }
  57. //virtual
  58. LLNameListCtrl::~LLNameListCtrl()
  59. {
  60. sInstances.erase(this);
  61. }
  62. bool LLNameListCtrl::addNameItem(const LLUUID& agent_id, EAddPosition pos,
  63. bool enabled, const std::string& suffix)
  64. {
  65. std::string fullname;
  66. bool result = getResidentName(agent_id, fullname);
  67. fullname.append(suffix);
  68. addStringUUIDItem(fullname, agent_id, pos, enabled);
  69. return result;
  70. }
  71. //virtual
  72. bool LLNameListCtrl::handleDragAndDrop(S32 x, S32 y, MASK mask, bool drop,
  73. EDragAndDropType cargo_type,
  74. void* cargo_data, EAcceptance* accept,
  75. std::string& tooltip_msg)
  76. {
  77. if (!mAllowCallingCardDrop)
  78. {
  79. return false;
  80. }
  81. if (cargo_type == DAD_CALLINGCARD)
  82. {
  83. if (drop)
  84. {
  85. LLInventoryItem* item = (LLInventoryItem*)cargo_data;
  86. addNameItem(item->getCreatorUUID());
  87. }
  88. *accept = ACCEPT_YES_MULTI;
  89. }
  90. else
  91. {
  92. *accept = ACCEPT_NO;
  93. if (tooltip_msg.empty())
  94. {
  95. if (!getToolTip().empty())
  96. {
  97. tooltip_msg = getToolTip();
  98. }
  99. else
  100. {
  101. // Backwards compatible English tooltip (should be overridden
  102. // in xml)
  103. tooltip_msg.assign("Drag a calling card here\nto add a resident.");
  104. }
  105. }
  106. }
  107. LL_DEBUGS("UserInput") << "dragAndDrop handled by LLNameListCtrl "
  108. << getName() << LL_ENDL;
  109. return true;
  110. }
  111. void LLNameListCtrl::addGroupNameItem(const LLUUID& group_id, EAddPosition pos,
  112. bool enabled)
  113. {
  114. std::string group_name;
  115. getGroupName(group_id, group_name);
  116. addStringUUIDItem(group_name, group_id, pos, enabled);
  117. }
  118. void LLNameListCtrl::addGroupNameItem(LLScrollListItem* item, EAddPosition pos)
  119. {
  120. std::string group_name;
  121. getGroupName(item->getUUID(), group_name);
  122. LLScrollListText* cell =
  123. (LLScrollListText*)item->getColumn(mNameColumnIndex);
  124. cell->setText(group_name);
  125. addItem(item, pos);
  126. }
  127. bool LLNameListCtrl::addNameItem(LLScrollListItem* item, EAddPosition pos)
  128. {
  129. std::string fullname;
  130. bool result = getResidentName(item->getUUID(), fullname);
  131. LLScrollListText* cell =
  132. (LLScrollListText*)item->getColumn(mNameColumnIndex);
  133. cell->setText(fullname);
  134. addItem(item, pos);
  135. // This column is resizable
  136. LLScrollListColumn* columnp = getColumn(mNameColumnIndex);
  137. if (columnp && columnp->mHeader)
  138. {
  139. columnp->mHeader->setHasResizableElement(true);
  140. }
  141. return result;
  142. }
  143. LLScrollListItem* LLNameListCtrl::addElement(const LLSD& value, EAddPosition pos,
  144. void* userdata)
  145. {
  146. LLScrollListItem* item = LLScrollListCtrl::addElement(value, pos,
  147. userdata);
  148. // Use supplied name by default
  149. std::string fullname = value["name"].asString();
  150. bool has_target = value.has("target");
  151. if (has_target && value["target"].asString() == "GROUP")
  152. {
  153. std::string name;
  154. if (getGroupName(item->getUUID(), name))
  155. {
  156. fullname = name;
  157. }
  158. }
  159. // When "SPECIAL", we just use supplied name, else it must be a resident
  160. else if (!has_target || value["target"].asString() != "SPECIAL")
  161. {
  162. // Normal resident
  163. std::string name;
  164. if (getResidentName(item->getUUID(), name))
  165. {
  166. fullname = name;
  167. }
  168. }
  169. LLScrollListText* cell =
  170. (LLScrollListText*)item->getColumn(mNameColumnIndex);
  171. cell->setText(fullname);
  172. dirtyColumns();
  173. // This column is resizable
  174. LLScrollListColumn* columnp = getColumn(mNameColumnIndex);
  175. if (columnp && columnp->mHeader)
  176. {
  177. columnp->mHeader->setHasResizableElement(true);
  178. }
  179. return item;
  180. }
  181. void LLNameListCtrl::removeNameItem(const LLUUID& agent_id)
  182. {
  183. if (selectByID(agent_id))
  184. {
  185. S32 index = getItemIndex(getFirstSelected());
  186. if (index >= 0)
  187. {
  188. deleteSingleItem(index);
  189. }
  190. }
  191. }
  192. void LLNameListCtrl::draw()
  193. {
  194. if (LLFrameTimer::getElapsedSeconds() - mLastUpdate >= (F64)mLazyUpdateInterval &&
  195. mPendingUpdates.size())
  196. {
  197. pending_map_t::iterator av_it;
  198. pending_map_t::iterator av_end = mPendingUpdates.end();
  199. for (item_list::iterator iter = getItemList().begin(),
  200. end = getItemList().end();
  201. iter != end; ++iter)
  202. {
  203. LLScrollListItem* item = *iter;
  204. av_it = mPendingUpdates.find(item->getUUID());
  205. if (av_it != av_end)
  206. {
  207. LLScrollListText* cell =
  208. (LLScrollListText*)item->getColumn(mNameColumnIndex);
  209. cell->setText(av_it->second);
  210. mPendingUpdates.erase(av_it);
  211. if (mPendingUpdates.size() == 0)
  212. {
  213. break;
  214. }
  215. av_end = mPendingUpdates.end();
  216. }
  217. }
  218. dirtyColumns();
  219. // Updates received via the legacy cache requests callback may not be
  220. // for us, so there may be ids left in mPendingUpdates that do not
  221. // belong to our list, also, some list lines may have been removed
  222. // while waiting for the name request reply: just trash them all.
  223. mPendingUpdates.clear();
  224. mLastUpdate = LLFrameTimer::getElapsedSeconds();
  225. }
  226. LLScrollListCtrl::draw();
  227. }
  228. void LLNameListCtrl::refresh(const LLUUID& id, const std::string& fullname,
  229. bool is_group)
  230. {
  231. if (mLazyUpdateInterval > 0.f)
  232. {
  233. // Perform a lazy update for names that can come in large amount (100+)
  234. // at a short interval of time.
  235. mPendingUpdates[id] = fullname;
  236. return;
  237. }
  238. for (item_list::iterator iter = getItemList().begin(),
  239. end = getItemList().end();
  240. iter != end; ++iter)
  241. {
  242. LLScrollListItem* item = *iter;
  243. if (item->getUUID() == id)
  244. {
  245. LLScrollListText* cell =
  246. (LLScrollListText*)item->getColumn(mNameColumnIndex);
  247. cell->setText(fullname);
  248. }
  249. }
  250. dirtyColumns();
  251. }
  252. //static
  253. void LLNameListCtrl::refreshAll(const LLUUID& id, const std::string& fullname,
  254. bool is_group)
  255. {
  256. for (instances_list_t::iterator it = sInstances.begin(),
  257. end = sInstances.end();
  258. it != end; ++it)
  259. {
  260. LLNameListCtrl* ctrl = *it;
  261. ctrl->refresh(id, fullname, is_group);
  262. }
  263. }
  264. //virtual
  265. const std::string& LLNameListCtrl::getTag() const
  266. {
  267. return LL_NAME_LIST_CTRL_TAG;
  268. }
  269. //virtual
  270. LLXMLNodePtr LLNameListCtrl::getXML(bool save_children) const
  271. {
  272. LLXMLNodePtr node = LLScrollListCtrl::getXML();
  273. node->setName(LL_NAME_LIST_CTRL_TAG);
  274. node->createChild("allow_calling_card_drop",
  275. true)->setBoolValue(mAllowCallingCardDrop);
  276. node->createChild("use_display_names",
  277. true)->setBoolValue(mUseDisplayNames);
  278. if (mNameColumnIndex)
  279. {
  280. node->createChild("name_column_index",
  281. true)->setIntValue(mNameColumnIndex);
  282. }
  283. // Do not save contents, probably filled by code
  284. return node;
  285. }
  286. LLView* LLNameListCtrl::fromXML(LLXMLNodePtr node, LLView* parent,
  287. LLUICtrlFactory* factory)
  288. {
  289. std::string name = LL_NAME_LIST_CTRL_TAG;
  290. node->getAttributeString("name", name);
  291. LLRect rect;
  292. createRect(node, rect, parent, LLRect());
  293. bool multi_select = false;
  294. node->getAttributeBool("multi_select", multi_select);
  295. bool draw_border = true;
  296. node->getAttributeBool("draw_border", draw_border);
  297. bool draw_heading = false;
  298. node->getAttributeBool("draw_heading", draw_heading);
  299. S32 name_column_index = 0;
  300. node->getAttributeS32("name_column_index", name_column_index);
  301. LLUICtrlCallback callback = NULL;
  302. LLNameListCtrl* name_list = new LLNameListCtrl(name, rect, callback, NULL,
  303. multi_select, draw_border,
  304. name_column_index);
  305. name_list->setDisplayHeading(draw_heading);
  306. if (node->hasAttribute("heading_height"))
  307. {
  308. S32 heading_height;
  309. node->getAttributeS32("heading_height", heading_height);
  310. name_list->setHeadingHeight(heading_height);
  311. }
  312. bool allow_calling_card_drop = false;
  313. if (node->getAttributeBool("allow_calling_card_drop",
  314. allow_calling_card_drop))
  315. {
  316. name_list->setAllowCallingCardDrop(allow_calling_card_drop);
  317. }
  318. if (node->hasAttribute("use_display_names"))
  319. {
  320. bool use_display_names = false;
  321. if (node->getAttributeBool("use_display_names", use_display_names))
  322. {
  323. name_list->setUseDisplayNames(use_display_names);
  324. }
  325. }
  326. name_list->setScrollListParameters(node);
  327. name_list->initFromXML(node, parent);
  328. LLSD columns;
  329. S32 index = 0;
  330. std::string labelname, columnname;
  331. LLXMLNodePtr child;
  332. for (child = node->getFirstChild(); child.notNull();
  333. child = child->getNextSibling())
  334. {
  335. if (child->hasName("column"))
  336. {
  337. labelname.clear();
  338. child->getAttributeString("label", labelname);
  339. columnname.clear();
  340. child->getAttributeString("name", columnname);
  341. if (columnname.empty())
  342. {
  343. columnname = labelname;
  344. }
  345. else if (labelname.empty())
  346. {
  347. labelname = columnname;
  348. }
  349. bool columndynamicwidth = false;
  350. child->getAttributeBool("dynamicwidth", columndynamicwidth);
  351. std::string sortname(columnname);
  352. child->getAttributeString("sort", sortname);
  353. S32 columnwidth = -1;
  354. if (child->hasAttribute("relwidth"))
  355. {
  356. F32 columnrelwidth = 0.f;
  357. child->getAttributeF32("relwidth", columnrelwidth);
  358. columns[index]["relwidth"] = columnrelwidth;
  359. }
  360. else
  361. {
  362. child->getAttributeS32("width", columnwidth);
  363. columns[index]["width"] = columnwidth;
  364. }
  365. LLFontGL::HAlign h_align = LLFontGL::LEFT;
  366. h_align = LLView::selectFontHAlign(child);
  367. columns[index]["name"] = columnname;
  368. columns[index]["label"] = labelname;
  369. columns[index]["halign"] = (S32)h_align;
  370. columns[index]["dynamicwidth"] = columndynamicwidth;
  371. columns[index++]["sort"] = sortname;
  372. }
  373. }
  374. name_list->setColumnHeadings(columns);
  375. LLUUID id;
  376. std::string value, font, font_style;
  377. for (child = node->getFirstChild(); child.notNull();
  378. child = child->getNextSibling())
  379. {
  380. if (child->hasName("row"))
  381. {
  382. child->getAttributeUUID("id", id);
  383. LLSD row;
  384. row["id"] = id;
  385. S32 column_idx = 0;
  386. for (LLXMLNodePtr row_child = node->getFirstChild();
  387. row_child.notNull(); row_child = row_child->getNextSibling())
  388. {
  389. if (row_child->hasName("column"))
  390. {
  391. value = row_child->getTextContents();
  392. columnname.clear();
  393. row_child->getAttributeString("name", columnname);
  394. font.clear();
  395. row_child->getAttributeString("font", font);
  396. font_style.clear();
  397. row_child->getAttributeString("font-style", font_style);
  398. LLSD& columns = row["columns"];
  399. columns[column_idx]["column"] = columnname;
  400. columns[column_idx]["value"] = value;
  401. columns[column_idx]["font"] = font;
  402. columns[column_idx++]["font-style"] = font_style;
  403. }
  404. }
  405. name_list->addElement(row);
  406. }
  407. }
  408. std::string contents = node->getTextContents();
  409. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  410. boost::char_separator<char> sep("\t\n");
  411. tokenizer tokens(contents, sep);
  412. tokenizer::iterator token_iter = tokens.begin();
  413. tokenizer::iterator token_end = tokens.end();
  414. while (token_iter != token_end)
  415. {
  416. const std::string& line = *token_iter++;
  417. name_list->addCommentText(line);
  418. }
  419. return name_list;
  420. }
  421. bool LLNameListCtrl::getResidentName(const LLUUID& agent_id,
  422. std::string& fullname)
  423. {
  424. LLAvatarName av_name;
  425. if (LLAvatarNameCache::get(agent_id, &av_name))
  426. {
  427. if (!LLAvatarName::sLegacyNamesForFriends && mUseDisplayNames &&
  428. LLAvatarNameCache::useDisplayNames())
  429. {
  430. if (LLAvatarNameCache::useDisplayNames() == 2)
  431. {
  432. fullname = av_name.mDisplayName;
  433. }
  434. else
  435. {
  436. fullname = av_name.getNames();
  437. }
  438. }
  439. else
  440. {
  441. fullname = av_name.getLegacyName();
  442. }
  443. return true;
  444. }
  445. // Schedule a callback
  446. LLAvatarNameCache::get(agent_id,
  447. boost::bind(&LLNameListCtrl::onAvatarNameCache,
  448. _1, _2, this));
  449. return false;
  450. }
  451. //static
  452. void LLNameListCtrl::onAvatarNameCache(const LLUUID& agent_id,
  453. const LLAvatarName& av_name,
  454. LLNameListCtrl* self)
  455. {
  456. if (!sInstances.count(self)) return; // Stale callback, instance closed.
  457. std::string fullname;
  458. if (!LLAvatarName::sLegacyNamesForFriends && self->mUseDisplayNames &&
  459. LLAvatarNameCache::useDisplayNames())
  460. {
  461. if (LLAvatarNameCache::useDisplayNames() == 2)
  462. {
  463. fullname = av_name.mDisplayName;
  464. }
  465. else
  466. {
  467. fullname = av_name.getNames();
  468. }
  469. }
  470. else
  471. {
  472. fullname = av_name.getLegacyName();
  473. }
  474. self->refresh(agent_id, fullname, false);
  475. }
  476. bool LLNameListCtrl::getGroupName(const LLUUID& group_id, std::string& name)
  477. {
  478. if (gCacheNamep->getGroupName(group_id, name))
  479. {
  480. return true;
  481. }
  482. // Schedule a callback
  483. gCacheNamep->get(group_id, true,
  484. boost::bind(&LLNameListCtrl::onGroupNameCache, _1, _2,
  485. this));
  486. return false;
  487. }
  488. //static
  489. void LLNameListCtrl::onGroupNameCache(const LLUUID& group_id,
  490. const std::string& name,
  491. LLNameListCtrl* self)
  492. {
  493. if (sInstances.count(self))
  494. {
  495. self->refresh(group_id, name, true);
  496. }
  497. }
  498. LLScrollListItem* LLNameListCtrl:: getItemById(const LLUUID& id)
  499. {
  500. for (item_list::iterator iter = getItemList().begin(),
  501. end = getItemList().end();
  502. iter != end; ++iter)
  503. {
  504. LLScrollListItem* item = *iter;
  505. if (item->getUUID() == id)
  506. {
  507. return item;
  508. }
  509. }
  510. return NULL;
  511. }
  512. void LLNameListCtrl::sortByName(bool ascending)
  513. {
  514. sortByColumnIndex(mNameColumnIndex, ascending);
  515. }