hbfloaterinvitemspicker.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /**
  2. * @file hbfloaterinvitemspicker.cpp
  3. * @brief Generic inventory items picker.
  4. * Also replaces LL's environment settings and materials pickers.
  5. *
  6. * $LicenseInfo:firstyear=2019&license=viewergpl$
  7. *
  8. * Copyright (c) 2019-2023, Henri Beauchamp
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "llviewerprecompiledheaders.h"
  34. #include <deque>
  35. #include "hbfloaterinvitemspicker.h"
  36. #include "llbutton.h"
  37. #include "llcheckboxctrl.h"
  38. #include "lllineeditor.h"
  39. #include "llscrolllistctrl.h"
  40. #include "lltextbox.h"
  41. #include "lltrans.h"
  42. #include "lluictrlfactory.h"
  43. #include "llagent.h" // For gAgentID
  44. #include "llinventorymodel.h"
  45. #include "llviewercontrol.h"
  46. HBFloaterInvItemsPicker::HBFloaterInvItemsPicker(LLView* ownerp, callback_t cb,
  47. void* userdata)
  48. : mCallback(cb),
  49. mCallbackUserdata(userdata),
  50. mPermissionMask(PERM_NONE), // No constraint on permissions.
  51. mAssetType(LLAssetType::AT_NONE), // No constraint on asset type.
  52. mSubType(-1), // No constraint on asset sub-type.
  53. mHasParentFloater(false),
  54. mAutoClose(true),
  55. mCallBackOnClose(false),
  56. mCanApplyImmediately(false)
  57. {
  58. LLUICtrlFactory::getInstance()->buildFloater(this,
  59. "floater_inv_items_picker.xml");
  60. // Note: at this point postBuild() has been called and returned.
  61. // Search for our owner's parent floater and register as dependent of
  62. // it if found.
  63. LLFloater* floaterp = ownerp ? ownerp->getParentFloater() : NULL;
  64. if (floaterp)
  65. {
  66. mHasParentFloater = true;
  67. floaterp->addDependentFloater(this);
  68. }
  69. else
  70. {
  71. // Place ourselves in a smart way, like preview floaters...
  72. S32 left, top;
  73. gFloaterViewp->getNewFloaterPosition(&left, &top);
  74. translate(left - getRect().mLeft, top - getRect().mTop);
  75. gFloaterViewp->adjustToFitScreen(this);
  76. }
  77. }
  78. //virtual
  79. HBFloaterInvItemsPicker::~HBFloaterInvItemsPicker()
  80. {
  81. gFocusMgr.releaseFocusIfNeeded(this);
  82. }
  83. //virtual
  84. bool HBFloaterInvItemsPicker::postBuild()
  85. {
  86. mSearchEditor = getChild<LLSearchEditor>("search_editor");
  87. mSearchEditor->setSearchCallback(onSearchEdit, this);
  88. mInventoryPanel = getChild<LLInventoryPanel>("inventory_panel");
  89. mInventoryPanel->setFollowsAll();
  90. mInventoryPanel->getRootFolder()->setCanAutoSelect(false);
  91. mInventoryPanel->setShowFolderState(LLInventoryFilter::SHOW_NON_EMPTY_FOLDERS);
  92. mInventoryPanel->setSelectCallback(onInventorySelectionChange, this);
  93. mApplyImmediatelyCheck = getChild<LLCheckBoxCtrl>("apply_immediate_check");
  94. mApplyImmediatelyCheck->setVisible(mCanApplyImmediately);
  95. mSelectToApplyText = getChild<LLTextBox>("select_to_apply_text");
  96. mSelectToApplyText->setVisible(!mCanApplyImmediately);
  97. mSelectButton = getChild<LLButton>("select_btn");
  98. mSelectButton->setClickedCallback(onBtnSelect, this);
  99. mSelectButton->setEnabled(false);
  100. childSetAction("close_btn", onBtnClose, this);
  101. setAllowMultiple(false);
  102. return true;
  103. }
  104. //virtual
  105. void HBFloaterInvItemsPicker::onClose(bool app_quitting)
  106. {
  107. if (!app_quitting && mCallBackOnClose)
  108. {
  109. // Send an empty selection on "Cancel" action.
  110. mCallback(std::vector<std::string>(), uuid_vec_t(), mCallbackUserdata,
  111. true);
  112. }
  113. LLFloater::onClose(app_quitting);
  114. }
  115. //virtual
  116. void HBFloaterInvItemsPicker::onFocusLost()
  117. {
  118. // NOTE: never auto-close when loosing focus if not parented
  119. if (mAutoClose && mHasParentFloater)
  120. {
  121. close();
  122. }
  123. else
  124. {
  125. LLFloater::onFocusLost();
  126. }
  127. }
  128. void HBFloaterInvItemsPicker::setAllowMultiple(bool allow_multiple)
  129. {
  130. mInventoryPanel->setAllowMultiSelect(allow_multiple);
  131. }
  132. void HBFloaterInvItemsPicker::setExcludeLibrary(bool exclude)
  133. {
  134. mInventoryPanel->setFilterHideLibrary(exclude);
  135. }
  136. void HBFloaterInvItemsPicker::allowApplyImmediately(bool enable)
  137. {
  138. mCanApplyImmediately = enable;
  139. mApplyImmediatelyCheck->setVisible(enable);
  140. mSelectToApplyText->setVisible(!enable);
  141. }
  142. bool HBFloaterInvItemsPicker::setApplyImmediately(bool checked)
  143. {
  144. if (mCanApplyImmediately)
  145. {
  146. mApplyImmediatelyCheck->set(checked);
  147. return true;
  148. }
  149. return false;
  150. }
  151. bool HBFloaterInvItemsPicker::setApplyImmediatelyControl(const char* ctrl_name)
  152. {
  153. LLControlVariable* controlp = gSavedSettings.getControl(ctrl_name);
  154. if (!controlp || controlp->type() != TYPE_BOOLEAN)
  155. {
  156. llwarns << "No such boolean global debug setting found: " << ctrl_name
  157. << llendl;
  158. return false;
  159. }
  160. allowApplyImmediately(true);
  161. setApplyImmediately(controlp->getValue().asBoolean());
  162. mApplyImmediatelyCheck->setControlName(ctrl_name, NULL);
  163. return true;
  164. }
  165. void HBFloaterInvItemsPicker::setAssetType(LLAssetType::EType type,
  166. S32 sub_type)
  167. {
  168. // Just in case: new asset type and sub-type may not be suitable for any
  169. // set Id.
  170. mSelectId.setNull();
  171. mAssetType = type;
  172. mSubType = sub_type;
  173. U32 filter = 1 << LLInventoryType::defaultForAssetType(type);
  174. mInventoryPanel->setFilterTypes(filter);
  175. mInventoryPanel->setFilterSubType(sub_type);
  176. mInventoryPanel->openDefaultFolderForType(type);
  177. // Set the floater title according to the type of asset we want to pick
  178. std::string type_name = LLAssetType::lookupHumanReadable(type);
  179. LLUIString title = getString("title");
  180. LLSD args;
  181. args["ASSETTYPE"] = LLTrans::getString(type_name);
  182. title.setArgs(args);
  183. setTitle(title);
  184. }
  185. void HBFloaterInvItemsPicker::setFilterPermMask(PermissionMask mask)
  186. {
  187. // Do not reapply the same mask to avoid pointeless refiltering.
  188. if (mask != mPermissionMask)
  189. {
  190. // Just in case: new permissions may not be suitable for any set Id.
  191. mSelectId.setNull();
  192. mPermissionMask = mask;
  193. mInventoryPanel->setFilterPermMask(mask);
  194. }
  195. }
  196. void HBFloaterInvItemsPicker::setSelection(const LLUUID& id)
  197. {
  198. mSelectId.setNull(); // Reset any pending selection
  199. LLViewerInventoryItem* itemp = gInventory.getItem(id);
  200. if (!itemp)
  201. {
  202. llwarns << "Could not find any inventory item for Id: " << id
  203. << llendl;
  204. return;
  205. }
  206. if (mAssetType != LLAssetType::AT_NONE && itemp->getType() != mAssetType)
  207. {
  208. llwarns << "Inventory item of wrong asset type for Id: " << id
  209. << llendl;
  210. return;
  211. }
  212. if (mSubType != -1 && itemp->getSubType() != mSubType)
  213. {
  214. llwarns << "Inventory item of wrong asset sub-type for Id: " << id
  215. << llendl;
  216. return;
  217. }
  218. if (mPermissionMask != PERM_NONE)
  219. {
  220. bool good_perms = true;
  221. const LLPermissions& perms = itemp->getPermissions();
  222. if (mPermissionMask & PERM_COPY)
  223. {
  224. good_perms = perms.allowCopyBy(gAgentID);
  225. }
  226. if (good_perms && (mPermissionMask & PERM_TRANSFER))
  227. {
  228. good_perms = perms.allowTransferBy(gAgentID);
  229. }
  230. if (good_perms && (mPermissionMask & PERM_MODIFY))
  231. {
  232. good_perms = perms.allowModifyBy(gAgentID);
  233. }
  234. if (!good_perms)
  235. {
  236. llwarns << "Inventory item of wrong permissions for Id: " << id
  237. << llendl;
  238. return;
  239. }
  240. }
  241. mSelectId = id;
  242. }
  243. //static
  244. void HBFloaterInvItemsPicker::onBtnSelect(void* userdata)
  245. {
  246. HBFloaterInvItemsPicker* self = (HBFloaterInvItemsPicker*)userdata;
  247. if (!self) return;
  248. self->mCallback(self->mSelectedInvNames, self->mSelectedInvIDs,
  249. self->mCallbackUserdata, self->mAutoClose);
  250. self->mInventoryPanel->setSelection(LLUUID::null, false);
  251. if (self->mAutoClose)
  252. {
  253. self->mAutoClose = self->mCallBackOnClose = false;
  254. self->close();
  255. }
  256. }
  257. //static
  258. void HBFloaterInvItemsPicker::onBtnClose(void* userdata)
  259. {
  260. HBFloaterInvItemsPicker* self = (HBFloaterInvItemsPicker*)userdata;
  261. if (self)
  262. {
  263. self->mAutoClose = false;
  264. self->close();
  265. }
  266. }
  267. void HBFloaterInvItemsPicker::onSearchEdit(const std::string& search_str,
  268. void* userdata)
  269. {
  270. HBFloaterInvItemsPicker* self = (HBFloaterInvItemsPicker*)userdata;
  271. if (!self) return; // Paranoia
  272. LLFolderView* folderp = self->mInventoryPanel->getRootFolder();
  273. if (search_str.empty())
  274. {
  275. if (self->mInventoryPanel->getFilterSubString().empty())
  276. {
  277. // Current and new filters are empty: nothing to do !
  278. return;
  279. }
  280. self->mSavedFolderState.setApply(true);
  281. folderp->applyFunctorRecursively(self->mSavedFolderState);
  282. // Add folder with current item to the list of previously opened
  283. // folders
  284. LLOpenFoldersWithSelection opener;
  285. folderp->applyFunctorRecursively(opener);
  286. folderp->scrollToShowSelection();
  287. }
  288. else if (self->mInventoryPanel->getFilterSubString().empty())
  289. {
  290. // User just typed the first letter in the search editor; save existing
  291. // folder open state.
  292. if (!folderp->isFilterModified())
  293. {
  294. self->mSavedFolderState.setApply(false);
  295. folderp->applyFunctorRecursively(self->mSavedFolderState);
  296. }
  297. }
  298. std::string uc_search_str = search_str;
  299. LLStringUtil::toUpper(uc_search_str);
  300. self->mInventoryPanel->setFilterSubString(uc_search_str);
  301. }
  302. //static
  303. void HBFloaterInvItemsPicker::onInventorySelectionChange(LLFolderView* folderp,
  304. bool, void* userdata)
  305. {
  306. HBFloaterInvItemsPicker* self = (HBFloaterInvItemsPicker*)userdata;
  307. if (!self || !folderp) // Paranoia
  308. {
  309. return;
  310. }
  311. const LLFolderView::selected_items_t& items = folderp->getSelectedItems();
  312. if (self->mSelectId.notNull())
  313. {
  314. bool selected = false;
  315. for (std::deque<LLFolderViewItem*>::const_iterator it = items.begin(),
  316. end = items.end();
  317. it != end; ++it)
  318. {
  319. LLFolderViewEventListener* listenerp = (*it)->getListener();
  320. if (listenerp && listenerp->getUUID() == self->mSelectId)
  321. {
  322. selected = true;
  323. break;
  324. }
  325. }
  326. if (!selected)
  327. {
  328. self->mInventoryPanel->setSelection(self->mSelectId, true);
  329. return;
  330. }
  331. self->mSelectId.setNull();
  332. }
  333. self->mSelectedInvIDs.clear();
  334. self->mSelectedInvNames.clear();
  335. for (std::deque<LLFolderViewItem*>::const_iterator it = items.begin(),
  336. end = items.end();
  337. it != end; ++it)
  338. {
  339. LLFolderViewEventListener* listenerp = (*it)->getListener();
  340. if (!listenerp) continue; // Paranoia
  341. LLInventoryType::EType type = listenerp->getInventoryType();
  342. if (type != LLInventoryType::IT_CATEGORY &&
  343. type != LLInventoryType::IT_ROOT_CATEGORY)
  344. {
  345. LLInventoryItem* itemp = gInventory.getItem(listenerp->getUUID());
  346. if (itemp) // Paranoia
  347. {
  348. self->mSelectedInvIDs.emplace_back(itemp->getUUID());
  349. self->mSelectedInvNames.emplace_back(listenerp->getName());
  350. }
  351. }
  352. }
  353. bool has_selection = !self->mSelectedInvIDs.empty();
  354. self->mSelectButton->setEnabled(has_selection);
  355. if (has_selection && self->mCanApplyImmediately &&
  356. self->mApplyImmediatelyCheck->get())
  357. {
  358. self->mCallback(self->mSelectedInvNames, self->mSelectedInvIDs,
  359. self->mCallbackUserdata, false);
  360. }
  361. }