llpreviewnotecard.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296
  1. /**
  2. * @file llpreviewnotecard.cpp
  3. * @brief Implementation of the notecard editor
  4. *
  5. * $LicenseInfo:firstyear=2002&license=viewergpl$
  6. *
  7. * Copyright (c) 2002-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 "llviewerprecompiledheaders.h"
  33. #include "llpreviewnotecard.h"
  34. #include "llassetstorage.h"
  35. #include "llbutton.h"
  36. #include "lldir.h"
  37. #include "hbexternaleditor.h"
  38. #include "llfilesystem.h"
  39. #include "llfontgl.h"
  40. #include "lliconctrl.h"
  41. #include "llinventory.h"
  42. #include "lllineeditor.h"
  43. #include "llmenugl.h"
  44. #include "llnotifications.h"
  45. #include "llscrollbar.h"
  46. #include "llspellcheck.h"
  47. #include "lluictrlfactory.h"
  48. #include "roles_constants.h"
  49. #include "llagent.h"
  50. #include "llappviewer.h" // For abortQuit()
  51. #include "llfloatersearchreplace.h"
  52. #include "llinventorymodel.h"
  53. #include "llselectmgr.h"
  54. #include "llviewerassetupload.h"
  55. #include "llviewercontrol.h"
  56. #include "llviewerinventory.h"
  57. #include "llviewerobjectlist.h"
  58. #include "llviewerregion.h"
  59. #include "llviewerstats.h"
  60. #include "llviewertexteditor.h"
  61. constexpr S32 PREVIEW_MIN_WIDTH = 2 * PREVIEW_BORDER +
  62. 2 * PREVIEW_BUTTON_WIDTH +
  63. 2 * PREVIEW_PAD + RESIZE_HANDLE_WIDTH;
  64. constexpr S32 PREVIEW_MIN_HEIGHT = 2 * PREVIEW_BORDER +
  65. 3 * (20 + PREVIEW_PAD) +
  66. 2 * SCROLLBAR_SIZE + 128;
  67. std::set<LLPreviewNotecard*> LLPreviewNotecard::sInstances;
  68. LLFontGL* LLPreviewNotecard::sCustomFont = NULL;
  69. LLPreviewNotecard::LLPreviewNotecard(const std::string& name,
  70. const LLRect& rect,
  71. const std::string& title,
  72. const LLUUID& item_id,
  73. const LLUUID& object_id,
  74. const LLUUID& asset_id,
  75. bool show_keep_discard,
  76. LLPointer<LLViewerInventoryItem> inv_item)
  77. : LLPreview(name, rect, title, item_id, object_id, true, PREVIEW_MIN_WIDTH,
  78. PREVIEW_MIN_HEIGHT, inv_item),
  79. mExternalEditor(NULL),
  80. mAssetID(asset_id),
  81. mNotecardItemID(item_id),
  82. mObjectID(object_id),
  83. mShowKeepDiscard(show_keep_discard)
  84. {
  85. sInstances.insert(this);
  86. LLRect curRect = rect;
  87. std::string file;
  88. if (!show_keep_discard && mAssetID.isNull())
  89. {
  90. const LLInventoryItem* item = getItem();
  91. if (item)
  92. {
  93. mAssetID = item->getAssetUUID();
  94. }
  95. }
  96. LLUICtrlFactory::getInstance()->buildFloater(this,
  97. "floater_preview_notecard.xml");
  98. // Only assert shape if not hosted in a multifloater
  99. if (!getHost())
  100. {
  101. reshape(curRect.getWidth(), curRect.getHeight());
  102. setRect(curRect);
  103. }
  104. setTitle(title);
  105. setNoteName(title);
  106. }
  107. //virtual
  108. LLPreviewNotecard::~LLPreviewNotecard()
  109. {
  110. sInstances.erase(this);
  111. if (mExternalEditor)
  112. {
  113. delete mExternalEditor;
  114. }
  115. if (!mTempFilename.empty())
  116. {
  117. LLFile::remove(mTempFilename);
  118. }
  119. }
  120. //virtual
  121. bool LLPreviewNotecard::postBuild()
  122. {
  123. mEditor = getChild<LLViewerTextEditor>("text_edit");
  124. mEditor->setWordWrap(true);
  125. mEditor->setSourceID(mNotecardItemID);
  126. mEditor->setHandleEditKeysDirectly(true);
  127. mEditor->setNotecardInfo(mNotecardItemID, mObjectID);
  128. mEditor->makePristine();
  129. // Use separate and possibly different colors for the note card editor. HB
  130. LLColor4 color = gColors.getColor("TextFgNotecardColor");
  131. mEditor->setFgColor(color);
  132. mEditor->setTextDefaultColor(color);
  133. mEditor->setReadOnlyFgColor(gColors.getColor("TextFgNotecardReadOnlyColor"));
  134. mEditor->setWriteableBgColor(gColors.getColor("TextBgNotecardColor"));
  135. mEditor->setReadOnlyBgColor(gColors.getColor("TextBgNotecardReadOnlyColor"));
  136. if (sCustomFont)
  137. {
  138. mEditor->setFont(sCustomFont);
  139. }
  140. mDescription = getChild<LLLineEditor>("desc");
  141. mDescription->setCommitCallback(LLPreview::onText);
  142. mDescription->setCallbackUserData(this);
  143. mDescription->setPrevalidate(LLLineEditor::prevalidatePrintableNotPipe);
  144. const LLInventoryItem* inv_item = getItem();
  145. if (inv_item)
  146. {
  147. mDescription->setText(inv_item->getDescription());
  148. }
  149. mSaveButton = getChild<LLButton>("save_btn");
  150. mSaveButton->setClickedCallback(onClickSave, this);
  151. if (mShowKeepDiscard)
  152. {
  153. childSetAction("keep_btn", onKeepBtn, this);
  154. childSetAction("discard_btn", onDiscardBtn, this);
  155. }
  156. else
  157. {
  158. childSetVisible("keep_btn", false);
  159. childSetVisible("discard_btn", false);
  160. }
  161. mLockIcon = getChild<LLIconCtrl>("lock");
  162. mLockIcon->setVisible(false);
  163. LLMenuItemCallGL* itemp = getChild<LLMenuItemCallGL>("load");
  164. itemp->setMenuCallback(onLoadFromFile, this);
  165. itemp->setEnabledCallback(enableSaveLoadFile);
  166. itemp = getChild<LLMenuItemCallGL>("save");
  167. itemp->setMenuCallback(onSaveToFile, this);
  168. itemp->setEnabledCallback(enableSaveLoadFile);
  169. itemp = getChild<LLMenuItemCallGL>("external");
  170. itemp->setMenuCallback(onEditExternal, this);
  171. itemp->setEnabledCallback(enableSaveLoadFile);
  172. itemp = getChild<LLMenuItemCallGL>("undo");
  173. itemp->setMenuCallback(onUndoMenu, this);
  174. itemp->setEnabledCallback(enableUndoMenu);
  175. itemp = getChild<LLMenuItemCallGL>("redo");
  176. itemp->setMenuCallback(onRedoMenu, this);
  177. itemp->setEnabledCallback(enableRedoMenu);
  178. itemp = getChild<LLMenuItemCallGL>("cut");
  179. itemp->setMenuCallback(onCutMenu, this);
  180. itemp->setEnabledCallback(enableCutMenu);
  181. itemp = getChild<LLMenuItemCallGL>("copy");
  182. itemp->setMenuCallback(onCopyMenu, this);
  183. itemp->setEnabledCallback(enableCopyMenu);
  184. itemp = getChild<LLMenuItemCallGL>("paste");
  185. itemp->setMenuCallback(onPasteMenu, this);
  186. itemp->setEnabledCallback(enablePasteMenu);
  187. itemp = getChild<LLMenuItemCallGL>("select_all");
  188. itemp->setMenuCallback(onSelectAllMenu, this);
  189. itemp->setEnabledCallback(enableSelectAllMenu);
  190. itemp = getChild<LLMenuItemCallGL>("deselect");
  191. itemp->setMenuCallback(onDeselectMenu, this);
  192. itemp->setEnabledCallback(enableDeselectMenu);
  193. itemp = getChild<LLMenuItemCallGL>("search");
  194. itemp->setMenuCallback(onSearchMenu, this);
  195. itemp->setEnabledCallback(NULL);
  196. LLMenuItemCheckGL* citemp = getChild<LLMenuItemCheckGL>("spelling");
  197. citemp->setMenuCallback(onSpellCheckMenu, this);
  198. citemp->setEnabledCallback(enableSpellCheckMenu);
  199. citemp->setCheckCallback(checkSpellCheckMenu);
  200. citemp->setValue(false);
  201. // Make sure the std::vector will not move in memory while we fill it up,
  202. // since we use pointers to its entries in our callbacks ! HB
  203. mCallbackData.reserve(16); // Using a power of two, but 10 should be fine.
  204. citemp = getChild<LLMenuItemCheckGL>("SansSerif");
  205. mCallbackData.emplace_back(this, citemp);
  206. citemp->setMenuCallback(onSetFontName, &mCallbackData.back());
  207. citemp->setCheckCallback(checkFontName);
  208. citemp->setValue(false);
  209. citemp = getChild<LLMenuItemCheckGL>("Helvetica");
  210. mCallbackData.emplace_back(this, citemp);
  211. citemp->setMenuCallback(onSetFontName, &mCallbackData.back());
  212. citemp->setCheckCallback(checkFontName);
  213. citemp->setValue(false);
  214. citemp = getChild<LLMenuItemCheckGL>("Courrier");
  215. mCallbackData.emplace_back(this, citemp);
  216. citemp->setMenuCallback(onSetFontName, &mCallbackData.back());
  217. citemp->setCheckCallback(checkFontName);
  218. citemp->setValue(false);
  219. citemp = getChild<LLMenuItemCheckGL>("Monospace");
  220. mCallbackData.emplace_back(this, citemp);
  221. citemp->setMenuCallback(onSetFontName, &mCallbackData.back());
  222. citemp->setCheckCallback(checkFontName);
  223. citemp->setValue(false);
  224. citemp = getChild<LLMenuItemCheckGL>("Tiny");
  225. mCallbackData.emplace_back(this, citemp);
  226. citemp->setMenuCallback(onSetFontSize, &mCallbackData.back());
  227. citemp->setCheckCallback(checkFontSize);
  228. citemp->setValue(false);
  229. citemp = getChild<LLMenuItemCheckGL>("Little");
  230. mCallbackData.emplace_back(this, citemp);
  231. citemp->setMenuCallback(onSetFontSize, &mCallbackData.back());
  232. citemp->setCheckCallback(checkFontSize);
  233. citemp->setValue(false);
  234. citemp = getChild<LLMenuItemCheckGL>("Small");
  235. mCallbackData.emplace_back(this, citemp);
  236. citemp->setMenuCallback(onSetFontSize, &mCallbackData.back());
  237. citemp->setCheckCallback(checkFontSize);
  238. citemp->setValue(false);
  239. citemp = getChild<LLMenuItemCheckGL>("Medium");
  240. mCallbackData.emplace_back(this, citemp);
  241. citemp->setMenuCallback(onSetFontSize, &mCallbackData.back());
  242. citemp->setCheckCallback(checkFontSize);
  243. citemp->setValue(false);
  244. citemp = getChild<LLMenuItemCheckGL>("Large");
  245. mCallbackData.emplace_back(this, citemp);
  246. citemp->setMenuCallback(onSetFontSize, &mCallbackData.back());
  247. citemp->setCheckCallback(checkFontSize);
  248. citemp->setValue(false);
  249. citemp = getChild<LLMenuItemCheckGL>("Huge");
  250. mCallbackData.emplace_back(this, citemp);
  251. citemp->setMenuCallback(onSetFontSize, &mCallbackData.back());
  252. citemp->setCheckCallback(checkFontSize);
  253. citemp->setValue(false);
  254. // Tell LLEditMenuHandler about our editor type: this will trigger a Lua
  255. // callback if one is configured for context menus. HB
  256. mEditor->setCustomMenuType("notecard");
  257. return true;
  258. }
  259. //static
  260. void LLPreviewNotecard::refreshCachedSettings()
  261. {
  262. std::string font_name = gSavedSettings.getString("NotecardEditorFont");
  263. if (font_name.empty())
  264. {
  265. sCustomFont = NULL;
  266. }
  267. else
  268. {
  269. sCustomFont = LLFontGL::getFont(font_name.c_str());
  270. }
  271. }
  272. //virtual
  273. void LLPreviewNotecard::draw()
  274. {
  275. mSaveButton->setEnabled(getEnabled() && !mEditor->isPristine());
  276. LLPreview::draw();
  277. }
  278. void LLPreviewNotecard::setNoteName(std::string name)
  279. {
  280. if (name.find("Note: ") == 0)
  281. {
  282. name = name.substr(6);
  283. }
  284. if (name.empty())
  285. {
  286. name = "untitled";
  287. }
  288. mNoteName = name;
  289. }
  290. //virtual
  291. void LLPreviewNotecard::setObjectID(const LLUUID& object_id)
  292. {
  293. LLPreview::setObjectID(object_id);
  294. mEditor->setNotecardObjectID(mObjectUUID);
  295. mEditor->makePristine();
  296. }
  297. bool LLPreviewNotecard::saveItem(LLPointer<LLInventoryItem>* itemptr)
  298. {
  299. LLInventoryItem* item = NULL;
  300. if (itemptr && itemptr->notNull())
  301. {
  302. item = (LLInventoryItem*)(*itemptr);
  303. }
  304. bool res = saveIfNeeded(item);
  305. if (res)
  306. {
  307. delete itemptr;
  308. }
  309. return res;
  310. }
  311. void LLPreviewNotecard::setEnabled(bool enabled)
  312. {
  313. mEditor->setEnabled(enabled);
  314. mLockIcon->setVisible(!enabled);
  315. mDescription->setEnabled(enabled);
  316. mSaveButton->setEnabled(enabled && !mEditor->isPristine());
  317. }
  318. //virtual
  319. bool LLPreviewNotecard::handleKeyHere(KEY key, MASK mask)
  320. {
  321. if (mask == MASK_CONTROL)
  322. {
  323. if (key == 'S')
  324. {
  325. saveIfNeeded();
  326. return true;
  327. }
  328. if (key == 'F')
  329. {
  330. LLFloaterSearchReplace::show(mEditor);
  331. return true;
  332. }
  333. }
  334. return LLPreview::handleKeyHere(key, mask);
  335. }
  336. //virtual
  337. bool LLPreviewNotecard::canClose()
  338. {
  339. if (mForceClose || mEditor->isPristine())
  340. {
  341. return true;
  342. }
  343. if (!mSaveDialogShown)
  344. {
  345. mSaveDialogShown = true;
  346. // Bring up view-modal dialog: Save changes ? Yes, No, Cancel
  347. gNotifications.add("SaveChanges", LLSD(), LLSD(),
  348. boost::bind(&LLPreviewNotecard::handleSaveChangesDialog,
  349. this, _1, _2));
  350. }
  351. return false;
  352. }
  353. //virtual
  354. void LLPreviewNotecard::inventoryChanged(LLViewerObject*,
  355. LLInventoryObject::object_list_t*,
  356. S32, void*)
  357. {
  358. removeVOInventoryListener();
  359. loadAsset();
  360. }
  361. const LLInventoryItem* LLPreviewNotecard::getDragItem()
  362. {
  363. return mEditor->getDragItem();
  364. }
  365. bool LLPreviewNotecard::hasEmbeddedInventory()
  366. {
  367. return mEditor->hasEmbeddedInventory();
  368. }
  369. void LLPreviewNotecard::refreshFromInventory()
  370. {
  371. LL_DEBUGS("Notecard") << "Refreshing from inventory" << LL_ENDL;
  372. loadAsset();
  373. }
  374. void LLPreviewNotecard::loadAsset()
  375. {
  376. // Request the asset.
  377. const LLInventoryItem* item = getItem();
  378. if (!item)
  379. {
  380. if (mObjectUUID.notNull() && mItemUUID.notNull())
  381. {
  382. LLViewerObject* objectp = gObjectList.findObject(mObjectUUID);
  383. if (objectp &&
  384. (objectp->isInventoryPending() || objectp->isInventoryDirty()))
  385. {
  386. // It is a notecard in an object inventory and we failed to get
  387. // it because inventory is not up to date. Subscribe for
  388. // callback and retry at inventoryChanged().
  389. // This also removes any previous listener:
  390. registerVOInventoryListener(objectp, NULL);
  391. if (objectp->isInventoryDirty())
  392. {
  393. objectp->requestInventory();
  394. }
  395. return;
  396. }
  397. }
  398. mEditor->setText(LLStringUtil::null);
  399. mEditor->makePristine();
  400. mEditor->setEnabled(true);
  401. #if 0 // Do not set the asset status here: we may not have set the item Id
  402. // yet (e.g. when this gets called initially)
  403. mAssetStatus = PREVIEW_ASSET_LOADED;
  404. #endif
  405. return;
  406. }
  407. if (gAgent.isGodlike() ||
  408. gAgent.allowOperation(PERM_COPY, item->getPermissions(),
  409. GP_OBJECT_MANIPULATE))
  410. {
  411. mAssetID = item->getAssetUUID();
  412. if (mAssetID.isNull())
  413. {
  414. mEditor->setText(LLStringUtil::null);
  415. mEditor->makePristine();
  416. mEditor->setEnabled(true);
  417. mAssetStatus = PREVIEW_ASSET_LOADED;
  418. }
  419. else if (gAssetStoragep)
  420. {
  421. LLHost source_sim = LLHost();
  422. if (mObjectUUID.notNull())
  423. {
  424. LLViewerObject* objectp = gObjectList.findObject(mObjectUUID);
  425. if (objectp && objectp->getRegion())
  426. {
  427. source_sim = objectp->getRegion()->getHost();
  428. }
  429. else
  430. {
  431. // The object that we are trying to look at
  432. // disappeared: bail out.
  433. llwarns << "Cannot find object " << mObjectUUID
  434. << " associated with notecard." << llendl;
  435. mAssetID.setNull();
  436. mEditor->setText(getString("no_object"));
  437. mEditor->makePristine();
  438. mEditor->setEnabled(false);
  439. mAssetStatus = PREVIEW_ASSET_LOADED;
  440. return;
  441. }
  442. }
  443. gAssetStoragep->getInvItemAsset(source_sim,
  444. gAgentID, gAgentSessionID,
  445. item->getPermissions().getOwner(),
  446. mObjectUUID, item->getUUID(),
  447. item->getAssetUUID(),
  448. item->getType(), onLoadComplete,
  449. (void*)new LLUUID(mItemUUID),
  450. true);
  451. mAssetStatus = PREVIEW_ASSET_LOADING;
  452. }
  453. }
  454. else
  455. {
  456. mAssetID.setNull();
  457. mEditor->setText(getString("not_allowed"));
  458. mEditor->makePristine();
  459. mEditor->setEnabled(false);
  460. mAssetStatus = PREVIEW_ASSET_LOADED;
  461. }
  462. if (!canModify(mObjectUUID, item))
  463. {
  464. mEditor->setEnabled(false);
  465. mLockIcon->setVisible(true);
  466. }
  467. }
  468. bool LLPreviewNotecard::loadFile(const std::string& filename)
  469. {
  470. if (filename.empty())
  471. {
  472. return false;
  473. }
  474. std::ifstream file(filename.c_str());
  475. if (file.fail())
  476. {
  477. return false;
  478. }
  479. mEditor->clear();
  480. std::string line, text;
  481. while (!file.eof())
  482. {
  483. std::getline(file, line);
  484. text += line + "\n";
  485. }
  486. file.close();
  487. LLWString wtext = utf8str_to_wstring(text);
  488. LLWStringUtil::replaceTabsWithSpaces(wtext, 4);
  489. text = wstring_to_utf8str(wtext);
  490. mEditor->setText(text);
  491. return true;
  492. }
  493. bool LLPreviewNotecard::saveFile(std::string& filename)
  494. {
  495. if (filename.empty())
  496. {
  497. return false;
  498. }
  499. std::string lcname = filename;
  500. LLStringUtil::toLower(lcname);
  501. if (lcname.find(".txt") != lcname.length() - 4)
  502. {
  503. filename += ".txt";
  504. }
  505. std::ofstream file(filename.c_str());
  506. if (file.fail())
  507. {
  508. LLSD args;
  509. args["FILE"] = filename;
  510. gNotifications.add("CannotWriteFile", args);
  511. return false;
  512. }
  513. file << mEditor->getText();
  514. file.close();
  515. return true;
  516. }
  517. //static
  518. void LLPreviewNotecard::onLoadComplete(const LLUUID& asset_id,
  519. LLAssetType::EType type, void* userdata,
  520. S32 status, LLExtStat)
  521. {
  522. LLUUID* item_id = (LLUUID*)userdata;
  523. LLPreviewNotecard* self = LLPreviewNotecard::getInstance(*item_id);
  524. delete item_id;
  525. if (!self)
  526. {
  527. return;
  528. }
  529. if (status)
  530. {
  531. gViewerStats.incStat(LLViewerStats::ST_DOWNLOAD_FAILED);
  532. if (status == LL_ERR_FILE_EMPTY ||
  533. status == LL_ERR_ASSET_REQUEST_NOT_IN_DATABASE)
  534. {
  535. gNotifications.add("NotecardMissing");
  536. }
  537. else if (status == LL_ERR_INSUFFICIENT_PERMISSIONS)
  538. {
  539. gNotifications.add("NotecardNoPermissions");
  540. }
  541. else
  542. {
  543. gNotifications.add("UnableToLoadNotecard");
  544. }
  545. self->mAssetStatus = PREVIEW_ASSET_ERROR;
  546. return;
  547. }
  548. S32 pos = 0;
  549. if (self->mEditor->getLength() > 0)
  550. {
  551. pos = self->mEditor->getCursorPos();
  552. }
  553. LLFileSystem file(asset_id);
  554. S32 file_length = file.getSize();
  555. char* bufferp = new char[file_length + 1];
  556. file.read((U8*)bufferp, file_length);
  557. // Put a EOS at the end
  558. bufferp[file_length] = 0;
  559. if (file_length > 19 && !strncmp(bufferp, "Linden text version", 19))
  560. {
  561. if (!self->mEditor->importBuffer(bufferp, file_length + 1))
  562. {
  563. llwarns << "Problem importing notecard" << llendl;
  564. }
  565. }
  566. else
  567. {
  568. // Version 0 (just text, does not include version number)
  569. self->mEditor->setText(std::string(bufferp));
  570. }
  571. delete[] bufferp;
  572. self->mEditor->makePristine();
  573. if (pos > 0)
  574. {
  575. self->mEditor->setCursorPos(pos);
  576. self->mEditor->scrollToPos(pos);
  577. }
  578. bool modifiable = canModify(self->mObjectUUID, self->getItem());
  579. self->setEnabled(modifiable);
  580. self->mAssetStatus = PREVIEW_ASSET_LOADED;
  581. }
  582. //static
  583. LLPreviewNotecard* LLPreviewNotecard::getInstance(const LLUUID& item_id)
  584. {
  585. preview_map_t::iterator it = LLPreview::sInstances.find(item_id);
  586. return it != LLPreview::sInstances.end() ? (LLPreviewNotecard*)it->second
  587. : NULL;
  588. }
  589. //static
  590. void LLPreviewNotecard::onClickSave(void* user_data)
  591. {
  592. LLPreviewNotecard* preview = (LLPreviewNotecard*)user_data;
  593. if (preview)
  594. {
  595. preview->saveIfNeeded();
  596. }
  597. }
  598. struct LLSaveNotecardInfo
  599. {
  600. LLSaveNotecardInfo(LLPreviewNotecard* self, const LLUUID& item_id,
  601. const LLUUID& object_id, const LLTransactionID& tid,
  602. LLInventoryItem* copyitem)
  603. : mSelf(self),
  604. mItemUUID(item_id),
  605. mObjectUUID(object_id),
  606. mTransactionID(tid),
  607. mCopyItem(copyitem)
  608. {
  609. }
  610. LLPreviewNotecard* mSelf;
  611. LLPointer<LLInventoryItem> mCopyItem;
  612. LLUUID mItemUUID;
  613. LLUUID mObjectUUID;
  614. LLTransactionID mTransactionID;
  615. };
  616. bool LLPreviewNotecard::saveIfNeeded(LLInventoryItem* copyitem)
  617. {
  618. if (mEditor->isPristine())
  619. {
  620. return true;
  621. }
  622. std::string buffer;
  623. if (!mEditor->exportBuffer(buffer))
  624. {
  625. return false;
  626. }
  627. mEditor->makePristine();
  628. if (mExternalEditor && mExternalEditor->running() && !mTempFilename.empty())
  629. {
  630. // Do not cause a file changed event for something we trigger ourselves
  631. // (the external editor will cause a file access read event, which is
  632. // considered a changed event, and would cause HBExternalEditor to call
  633. // our own changed file event, which we do not want to happen here).
  634. mExternalEditor->ignoreNextUpdate();
  635. saveFile(mTempFilename);
  636. }
  637. // Save it out to database
  638. const LLInventoryItem* item = getItem();
  639. if (!item)
  640. {
  641. return true;
  642. }
  643. // First try via HTTP capabilities.
  644. const std::string* urlp = NULL;
  645. LLResourceUploadInfo::ptr_t infop;
  646. if (mObjectUUID.isNull())
  647. {
  648. const std::string& inv_url =
  649. gAgent.getRegionCapability("UpdateNotecardAgentInventory");
  650. if (!inv_url.empty())
  651. {
  652. // Saving into agent inventory
  653. urlp = &inv_url;
  654. infop = std::make_shared<LLBufferedAssetUploadInfo>(
  655. mItemUUID, LLAssetType::AT_NOTECARD, buffer,
  656. boost::bind(&LLPreviewNotecard::finishInventoryUpload,
  657. _1, _2, _3));
  658. }
  659. }
  660. else
  661. {
  662. const std::string& task_url =
  663. gAgent.getRegionCapability("UpdateNotecardTaskInventory");
  664. if (!task_url.empty())
  665. {
  666. // Saving into task inventory
  667. urlp = &task_url;
  668. infop = std::make_shared<LLBufferedAssetUploadInfo>(
  669. mObjectUUID, mItemUUID, LLAssetType::AT_NOTECARD,
  670. buffer,
  671. boost::bind(&LLPreviewNotecard::finishTaskUpload,
  672. _1, _3));
  673. }
  674. }
  675. if (urlp && infop)
  676. {
  677. mAssetStatus = PREVIEW_ASSET_LOADING;
  678. setEnabled(false);
  679. LLViewerAssetUpload::enqueueInventoryUpload(*urlp, infop);
  680. return true;
  681. }
  682. // Legacy UDP upload path.
  683. if (gAssetStoragep)
  684. {
  685. // We need to update the asset information
  686. LLTransactionID tid;
  687. tid.generate();
  688. LLAssetID asset_id = tid.makeAssetID(gAgent.getSecureSessionID());
  689. LLFileSystem file(asset_id, LLFileSystem::APPEND);
  690. S32 size = buffer.length() + 1;
  691. if (!file.write((U8*)buffer.c_str(), size))
  692. {
  693. llwarns << "Failure to write cache file for asset: " << mAssetID
  694. << llendl;
  695. return false;
  696. }
  697. LLSaveNotecardInfo* info = new LLSaveNotecardInfo(this, mItemUUID,
  698. mObjectUUID, tid,
  699. copyitem);
  700. gAssetStoragep->storeAssetData(tid, LLAssetType::AT_NOTECARD,
  701. onSaveComplete, (void*)info, false);
  702. return true;
  703. }
  704. llwarns << "No capability neither asset storage system. Could not save notecard: "
  705. << mAssetID << llendl;
  706. return false;
  707. }
  708. //static
  709. void LLPreviewNotecard::finishTaskUpload(LLUUID item_id, LLUUID new_asset_id)
  710. {
  711. LLPreviewNotecard* self = LLPreviewNotecard::getInstance(item_id);
  712. if (self)
  713. {
  714. if (self->hasEmbeddedInventory())
  715. {
  716. LLFileSystem::removeFile(new_asset_id);
  717. }
  718. self->setAssetId(new_asset_id);
  719. self->refreshFromInventory();
  720. }
  721. }
  722. //static
  723. void LLPreviewNotecard::finishInventoryUpload(LLUUID item_id,
  724. LLUUID new_asset_id,
  725. LLUUID new_item_id)
  726. {
  727. // Update the UI with the new asset.
  728. LLPreviewNotecard* self = LLPreviewNotecard::getInstance(item_id);
  729. if (self)
  730. {
  731. // *HACK: we have to delete the asset in the cache so that the viewer
  732. // will re-download it. This is only really necessary if the asset had
  733. // to be modified by the uploader, so this can be optimized away in
  734. // some cases. A better design is to have a new uuid if the notecard
  735. // actually changed the asset.
  736. if (self->hasEmbeddedInventory())
  737. {
  738. LLFileSystem::removeFile(new_asset_id);
  739. }
  740. if (new_item_id.isNull())
  741. {
  742. self->setAssetId(new_asset_id);
  743. self->refreshFromInventory();
  744. }
  745. else
  746. {
  747. self->setItemID(new_item_id);
  748. self->refreshFromInventory();
  749. }
  750. }
  751. }
  752. //static
  753. void LLPreviewNotecard::onSaveComplete(const LLUUID& asset_id, void* user_data,
  754. S32 status, LLExtStat)
  755. {
  756. LLSaveNotecardInfo* info = (LLSaveNotecardInfo*)user_data;
  757. if (info && status == 0)
  758. {
  759. if (info->mObjectUUID.isNull())
  760. {
  761. LLViewerInventoryItem* item;
  762. item = (LLViewerInventoryItem*)gInventory.getItem(info->mItemUUID);
  763. if (item)
  764. {
  765. LLPointer<LLViewerInventoryItem> new_item =
  766. new LLViewerInventoryItem(item);
  767. new_item->setAssetUUID(asset_id);
  768. new_item->setTransactionID(info->mTransactionID);
  769. new_item->updateServer(false);
  770. gInventory.updateItem(new_item);
  771. gInventory.notifyObservers();
  772. }
  773. else
  774. {
  775. llwarns << "Inventory item for notecard " << info->mItemUUID
  776. << " is no longer in agent inventory." << llendl;
  777. }
  778. }
  779. else
  780. {
  781. LLViewerObject* object = gObjectList.findObject(info->mObjectUUID);
  782. LLViewerInventoryItem* item = NULL;
  783. if (object)
  784. {
  785. LLInventoryObject* inv_obj =
  786. object->getInventoryObject(info->mItemUUID);
  787. item = (LLViewerInventoryItem*)inv_obj;
  788. }
  789. if (item)
  790. {
  791. item->setAssetUUID(asset_id);
  792. item->setTransactionID(info->mTransactionID);
  793. object->updateInventory(item);
  794. dialog_refresh_all();
  795. }
  796. else
  797. {
  798. gNotifications.add("SaveNotecardFailObjectNotFound");
  799. }
  800. }
  801. // Perform item copy to inventory
  802. if (info->mCopyItem.notNull() && info->mSelf && info->mSelf->mEditor)
  803. {
  804. info->mSelf->mEditor->copyInventory(info->mCopyItem);
  805. }
  806. // Find our window and close it if requested.
  807. LLPreviewNotecard* previewp =
  808. (LLPreviewNotecard*)LLPreview::find(info->mItemUUID);
  809. if (previewp && previewp->mCloseAfterSave)
  810. {
  811. previewp->close();
  812. }
  813. }
  814. else
  815. {
  816. LLSD args;
  817. args["REASON"] = std::string(LLAssetStorage::getErrorString(status));
  818. gNotifications.add("SaveNotecardFailReason", args);
  819. }
  820. std::string filename = gDirUtil.getFullPath(LL_PATH_CACHE,
  821. asset_id.asString());
  822. LLFile::remove(filename + ".tmp");
  823. delete info;
  824. }
  825. bool LLPreviewNotecard::handleSaveChangesDialog(const LLSD& notification,
  826. const LLSD& response)
  827. {
  828. mSaveDialogShown = false;
  829. S32 option = LLNotification::getSelectedOption(notification, response);
  830. switch (option)
  831. {
  832. case 0: // "Yes"
  833. mCloseAfterSave = true;
  834. onClickSave((void*)this);
  835. break;
  836. case 1: // "No"
  837. mForceClose = true;
  838. close();
  839. break;
  840. case 2: // "Cancel"
  841. default:
  842. // If we were quitting, we did not really mean it.
  843. gAppViewerp->abortQuit();
  844. }
  845. return false;
  846. }
  847. void LLPreviewNotecard::reshape(S32 width, S32 height, bool called_from_parent)
  848. {
  849. LLPreview::reshape(width, height, called_from_parent);
  850. if (!isMinimized())
  851. {
  852. // So that next time you open a notecard it will have the same height
  853. // and width (although not the same position).
  854. gSavedSettings.setRect("NotecardEditorRect", getRect());
  855. }
  856. }
  857. //static
  858. bool LLPreviewNotecard::hasChanged(void* userdata)
  859. {
  860. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  861. return self && self->getEnabled() && !self->mEditor->isPristine();
  862. }
  863. //static
  864. bool LLPreviewNotecard::enableSaveLoadFile(void* userdata)
  865. {
  866. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  867. return self && self->getEnabled() && !HBFileSelector::isInUse();
  868. }
  869. //static
  870. void LLPreviewNotecard::loadFromFileCallback(HBFileSelector::ELoadFilter,
  871. std::string& filename,
  872. void* userdata)
  873. {
  874. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  875. if (!self || !sInstances.count(self))
  876. {
  877. gNotifications.add("LoadNoteAborted");
  878. return;
  879. }
  880. if (!self->loadFile(filename))
  881. {
  882. LLSD args;
  883. args["FILE"] = filename;
  884. gNotifications.add("CannotReadFile", args);
  885. }
  886. }
  887. //static
  888. void LLPreviewNotecard::onLoadFromFile(void* userdata)
  889. {
  890. HBFileSelector::loadFile(HBFileSelector::FFLOAD_TEXT, loadFromFileCallback,
  891. userdata);
  892. }
  893. //static
  894. void LLPreviewNotecard::saveToFileCallback(HBFileSelector::ESaveFilter,
  895. std::string& filename,
  896. void* userdata)
  897. {
  898. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  899. if (self && sInstances.count(self))
  900. {
  901. self->saveFile(filename);
  902. }
  903. else
  904. {
  905. gNotifications.add("SaveNoteAborted");
  906. }
  907. }
  908. //static
  909. void LLPreviewNotecard::onSaveToFile(void* userdata)
  910. {
  911. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  912. if (self && sInstances.count(self))
  913. {
  914. std::string suggestion = self->mNoteName + ".txt";
  915. HBFileSelector::saveFile(HBFileSelector::FFSAVE_TXT, suggestion,
  916. saveToFileCallback, userdata);
  917. }
  918. }
  919. //static
  920. void LLPreviewNotecard::onEditedFileChanged(const std::string& filename,
  921. void* userdata)
  922. {
  923. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  924. if (self && sInstances.count(self))
  925. {
  926. if (filename == self->mTempFilename)
  927. {
  928. self->loadFile(filename);
  929. }
  930. else
  931. {
  932. llwarns << "Watched file (" << filename
  933. << ") and auto-saved file (" << self->mTempFilename
  934. << ") do not match !" << llendl;
  935. }
  936. }
  937. }
  938. //static
  939. void LLPreviewNotecard::onEditExternal(void* userdata)
  940. {
  941. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  942. if (self && sInstances.count(self))
  943. {
  944. if (self->mTempFilename.empty())
  945. {
  946. self->mTempFilename = gDirUtil.getTempFilename(false) + ".txt";
  947. }
  948. if (!self->saveFile(self->mTempFilename))
  949. {
  950. return;
  951. }
  952. if (self->mExternalEditor)
  953. {
  954. self->mExternalEditor->kill();
  955. }
  956. else
  957. {
  958. self->mExternalEditor = new HBExternalEditor(onEditedFileChanged,
  959. self);
  960. }
  961. if (!self->mExternalEditor->open(self->mTempFilename))
  962. {
  963. LLSD args;
  964. args["MESSAGE"] = self->mExternalEditor->getErrorMessage();
  965. gNotifications.add("GenericAlert", args);
  966. }
  967. }
  968. }
  969. //static
  970. void LLPreviewNotecard::onSearchMenu(void* userdata)
  971. {
  972. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  973. if (self)
  974. {
  975. LLFloaterSearchReplace::show(self->mEditor);
  976. }
  977. }
  978. //static
  979. void LLPreviewNotecard::onUndoMenu(void* userdata)
  980. {
  981. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  982. if (self)
  983. {
  984. self->mEditor->undo();
  985. }
  986. }
  987. //static
  988. void LLPreviewNotecard::onRedoMenu(void* userdata)
  989. {
  990. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  991. if (self)
  992. {
  993. self->mEditor->redo();
  994. }
  995. }
  996. //static
  997. void LLPreviewNotecard::onCutMenu(void* userdata)
  998. {
  999. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1000. if (self)
  1001. {
  1002. self->mEditor->cut();
  1003. }
  1004. }
  1005. //static
  1006. void LLPreviewNotecard::onCopyMenu(void* userdata)
  1007. {
  1008. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1009. if (self)
  1010. {
  1011. self->mEditor->copy();
  1012. }
  1013. }
  1014. //static
  1015. void LLPreviewNotecard::onPasteMenu(void* userdata)
  1016. {
  1017. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1018. if (self)
  1019. {
  1020. self->mEditor->paste();
  1021. }
  1022. }
  1023. //static
  1024. void LLPreviewNotecard::onSelectAllMenu(void* userdata)
  1025. {
  1026. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1027. if (self)
  1028. {
  1029. self->mEditor->selectAll();
  1030. }
  1031. }
  1032. //static
  1033. void LLPreviewNotecard::onDeselectMenu(void* userdata)
  1034. {
  1035. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1036. if (self)
  1037. {
  1038. self->mEditor->deselect();
  1039. }
  1040. }
  1041. //static
  1042. void LLPreviewNotecard::onSpellCheckMenu(void* userdata)
  1043. {
  1044. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1045. if (self)
  1046. {
  1047. self->mEditor->setSpellCheck(!self->mEditor->getSpellCheck());
  1048. }
  1049. }
  1050. //static
  1051. bool LLPreviewNotecard::enableUndoMenu(void* userdata)
  1052. {
  1053. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1054. return self && self->mEditor->canUndo();
  1055. }
  1056. //static
  1057. bool LLPreviewNotecard::enableRedoMenu(void* userdata)
  1058. {
  1059. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1060. return self && self->mEditor->canRedo();
  1061. }
  1062. //static
  1063. bool LLPreviewNotecard::enableCutMenu(void* userdata)
  1064. {
  1065. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1066. return self && self->mEditor->canCut();
  1067. }
  1068. //static
  1069. bool LLPreviewNotecard::enableCopyMenu(void* userdata)
  1070. {
  1071. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1072. return self && self->mEditor->canCopy();
  1073. }
  1074. //static
  1075. bool LLPreviewNotecard::enablePasteMenu(void* userdata)
  1076. {
  1077. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1078. return self && self->mEditor->canPaste();
  1079. }
  1080. //static
  1081. bool LLPreviewNotecard::enableSelectAllMenu(void* userdata)
  1082. {
  1083. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1084. return self && self->mEditor->canSelectAll();
  1085. }
  1086. //static
  1087. bool LLPreviewNotecard::enableDeselectMenu(void* userdata)
  1088. {
  1089. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1090. return self && self->mEditor->canDeselect();
  1091. }
  1092. //static
  1093. bool LLPreviewNotecard::enableSpellCheckMenu(void* userdata)
  1094. {
  1095. return LLSpellCheck::getInstance()->getSpellCheck();
  1096. }
  1097. //static
  1098. bool LLPreviewNotecard::checkSpellCheckMenu(void* userdata)
  1099. {
  1100. LLPreviewNotecard* self = (LLPreviewNotecard*)userdata;
  1101. return self && self->mEditor->getSpellCheck() &&
  1102. LLSpellCheck::getInstance()->getSpellCheck();
  1103. }
  1104. //static
  1105. void LLPreviewNotecard::onSetFontName(void* userdata)
  1106. {
  1107. CallbackData* datap = (CallbackData*)userdata;
  1108. LLPreviewNotecard* self = datap->mFloater;
  1109. LLFontGL* fontp = self->mEditor->getFont();
  1110. if (!fontp) // Paranoia
  1111. {
  1112. return;
  1113. }
  1114. std::string font_name = datap->mCheck->getName() +
  1115. fontp->getFontDesc().getSize();
  1116. fontp = LLFontGL::getFont(font_name.c_str());
  1117. if (fontp)
  1118. {
  1119. self->mEditor->setFont(fontp);
  1120. }
  1121. }
  1122. //static
  1123. bool LLPreviewNotecard::checkFontName(void* userdata)
  1124. {
  1125. CallbackData* datap = (CallbackData*)userdata;
  1126. LLPreviewNotecard* self = datap->mFloater;
  1127. LLFontGL* fontp = self->mEditor->getFont();
  1128. #if LL_WINDOWS
  1129. std::string font_name = datap->mCheck->getName();
  1130. // Under Windows, font names are lower-cased because they are compared with
  1131. // file names which are case-insensitive...
  1132. LLStringUtil::toLower(font_name);
  1133. return fontp && fontp->getFontDesc().getName() == font_name;
  1134. #else
  1135. return fontp && fontp->getFontDesc().getName() == datap->mCheck->getName();
  1136. #endif
  1137. }
  1138. //static
  1139. void LLPreviewNotecard::onSetFontSize(void* userdata)
  1140. {
  1141. CallbackData* datap = (CallbackData*)userdata;
  1142. LLPreviewNotecard* self = datap->mFloater;
  1143. LLFontGL* fontp = self->mEditor->getFont();
  1144. if (!fontp) // Paranoia
  1145. {
  1146. return;
  1147. }
  1148. std::string font_name = fontp->getFontDesc().getName() +
  1149. datap->mCheck->getName();
  1150. fontp = LLFontGL::getFont(font_name.c_str());
  1151. if (fontp)
  1152. {
  1153. self->mEditor->setFont(fontp);
  1154. }
  1155. }
  1156. //static
  1157. bool LLPreviewNotecard::checkFontSize(void* userdata)
  1158. {
  1159. CallbackData* datap = (CallbackData*)userdata;
  1160. LLPreviewNotecard* self = datap->mFloater;
  1161. LLFontGL* fontp = self->mEditor->getFont();
  1162. return fontp && fontp->getFontDesc().getSize() == datap->mCheck->getName();
  1163. }