PageRenderTime 54ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 1ms

/indra/newview/llfloateruipreview.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 1766 lines | 1354 code | 206 blank | 206 comment | 217 complexity | 8da1e9bb2a25d4f9e245b4cebca0ee44 MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llfloateruipreview.cpp
  3. * @brief Tool for previewing and editing floaters, plus localization tool integration
  4. *
  5. * $LicenseInfo:firstyear=2008&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. // Tool for previewing floaters and panels for localization and UI design purposes.
  27. // See: https://wiki.lindenlab.com/wiki/GUI_Preview_And_Localization_Tools
  28. // See: https://jira.lindenlab.com/browse/DEV-16869
  29. // *TODO: Translate error messgaes using notifications/alerts.xml
  30. #include "llviewerprecompiledheaders.h" // Precompiled headers
  31. #include "llfloateruipreview.h" // Own header
  32. // Internal utility
  33. #include "lldiriterator.h"
  34. #include "lleventtimer.h"
  35. #include "llexternaleditor.h"
  36. #include "llrender.h"
  37. #include "llsdutil.h"
  38. #include "llxmltree.h"
  39. #include "llviewerwindow.h"
  40. // XUI
  41. #include "lluictrlfactory.h"
  42. #include "llcombobox.h"
  43. #include "llnotificationsutil.h"
  44. #include "llresizebar.h"
  45. #include "llscrolllistitem.h"
  46. #include "llscrolllistctrl.h"
  47. #include "llfilepicker.h"
  48. #include "lldraghandle.h"
  49. #include "lllayoutstack.h"
  50. #include "lltooltip.h"
  51. #include "llviewermenu.h"
  52. #include "llrngwriter.h"
  53. #include "llfloater.h" // superclass
  54. #include "llfloaterreg.h"
  55. #include "llscrollcontainer.h" // scroll container for overlapping elements
  56. #include "lllivefile.h" // live file poll/stat/reload
  57. // Boost (for linux/unix command-line execv)
  58. #include <boost/tokenizer.hpp>
  59. #include <boost/shared_ptr.hpp>
  60. // External utility
  61. #include <string>
  62. #include <list>
  63. #include <map>
  64. #if LL_DARWIN
  65. #include <CoreFoundation/CFURL.h>
  66. #endif
  67. // Static initialization
  68. static const S32 PRIMARY_FLOATER = 1;
  69. static const S32 SECONDARY_FLOATER = 2;
  70. class LLOverlapPanel;
  71. static LLDefaultChildRegistry::Register<LLOverlapPanel> register_overlap_panel("overlap_panel");
  72. static std::string get_xui_dir()
  73. {
  74. std::string delim = gDirUtilp->getDirDelimiter();
  75. return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim;
  76. }
  77. // Forward declarations to avoid header dependencies
  78. class LLColor;
  79. class LLScrollListCtrl;
  80. class LLComboBox;
  81. class LLButton;
  82. class LLLineEditor;
  83. class LLXmlTreeNode;
  84. class LLFloaterUIPreview;
  85. class LLFadeEventTimer;
  86. class LLLocalizationResetForcer;
  87. class LLGUIPreviewLiveFile;
  88. class LLFadeEventTimer;
  89. class LLPreviewedFloater;
  90. // Implementation of custom overlapping element display panel
  91. class LLOverlapPanel : public LLPanel
  92. {
  93. public:
  94. struct Params : public LLInitParam::Block<Params, LLPanel::Params>
  95. {
  96. Params() {}
  97. };
  98. LLOverlapPanel(Params p = Params()) : LLPanel(p),
  99. mSpacing(10),
  100. // mClickedElement(NULL),
  101. mLastClickedElement(NULL)
  102. {
  103. mOriginalWidth = getRect().getWidth();
  104. mOriginalHeight = getRect().getHeight();
  105. }
  106. virtual void draw();
  107. typedef std::map<LLView*, std::list<LLView*> > OverlapMap;
  108. OverlapMap mOverlapMap; // map, of XUI element to a list of XUI elements it overlaps
  109. // LLView *mClickedElement;
  110. LLView *mLastClickedElement;
  111. int mOriginalWidth, mOriginalHeight, mSpacing;
  112. };
  113. class LLFloaterUIPreview : public LLFloater
  114. {
  115. public:
  116. // Setup
  117. LLFloaterUIPreview(const LLSD& key);
  118. virtual ~LLFloaterUIPreview();
  119. std::string getLocStr(S32 ID); // fetches the localization string based on what is selected in the drop-down menu
  120. void displayFloater(BOOL click, S32 ID, bool save = false); // needs to be public so live file can call it when it finds an update
  121. /*virtual*/ BOOL postBuild();
  122. /*virtual*/ void onClose(bool app_quitting);
  123. void refreshList(); // refresh list (empty it out and fill it up from scratch)
  124. void addFloaterEntry(const std::string& path); // add a single file's entry to the list of floaters
  125. static BOOL containerType(LLView* viewp); // check if the element is a container type and tree traverses need to look at its children
  126. public:
  127. LLPreviewedFloater* mDisplayedFloater; // the floater which is currently being displayed
  128. LLPreviewedFloater* mDisplayedFloater_2; // the floater which is currently being displayed
  129. LLGUIPreviewLiveFile* mLiveFile; // live file for checking for updates to the currently-displayed XML file
  130. LLOverlapPanel* mOverlapPanel; // custom overlapping elements panel
  131. // BOOL mHighlightingDiffs; // bool for whether localization diffs are being highlighted or not
  132. BOOL mHighlightingOverlaps; // bool for whether overlapping elements are being highlighted
  133. // typedef std::map<std::string,std::pair<std::list<std::string>,std::list<std::string> > > DiffMap; // this version copies the lists etc., and thus is bad memory-wise
  134. typedef std::list<std::string> StringList;
  135. typedef boost::shared_ptr<StringList> StringListPtr;
  136. typedef std::map<std::string, std::pair<StringListPtr,StringListPtr> > DiffMap;
  137. DiffMap mDiffsMap; // map, of filename to pair of list of changed element paths and list of errors
  138. private:
  139. LLExternalEditor mExternalEditor;
  140. // XUI elements for this floater
  141. LLScrollListCtrl* mFileList; // scroll list control for file list
  142. LLLineEditor* mEditorPathTextBox; // text field for path to editor executable
  143. LLLineEditor* mEditorArgsTextBox; // text field for arguments to editor executable
  144. LLLineEditor* mDiffPathTextBox; // text field for path to diff file
  145. LLButton* mDisplayFloaterBtn; // button to display primary floater
  146. LLButton* mDisplayFloaterBtn_2; // button to display secondary floater
  147. LLButton* mEditFloaterBtn; // button to edit floater
  148. LLButton* mExecutableBrowseButton; // button to browse for executable
  149. LLButton* mCloseOtherButton; // button to close primary displayed floater
  150. LLButton* mCloseOtherButton_2; // button to close secondary displayed floater
  151. LLButton* mDiffBrowseButton; // button to browse for diff file
  152. LLButton* mToggleHighlightButton; // button to toggle highlight of files/elements with diffs
  153. LLButton* mToggleOverlapButton; // button to togle overlap panel/highlighting
  154. LLComboBox* mLanguageSelection; // combo box for primary language selection
  155. LLComboBox* mLanguageSelection_2; // combo box for secondary language selection
  156. LLScrollContainer* mOverlapScrollView; // overlapping elements scroll container
  157. S32 mLastDisplayedX, mLastDisplayedY; // stored position of last floater so the new one opens up in the same place
  158. std::string mDelim; // the OS-specific delimiter character (/ or \) (*TODO: this shouldn't be needed, right?)
  159. std::string mSavedEditorPath; // stored editor path so closing this floater doesn't reset it
  160. std::string mSavedEditorArgs; // stored editor args so closing this floater doesn't reset it
  161. std::string mSavedDiffPath; // stored diff file path so closing this floater doesn't reset it
  162. // Internal functionality
  163. static void popupAndPrintWarning(const std::string& warning); // pop up a warning
  164. std::string getLocalizedDirectory(); // build and return the path to the XUI directory for the currently-selected localization
  165. void scanDiffFile(LLXmlTreeNode* file_node); // scan a given XML node for diff entries and highlight them in its associated file
  166. void highlightChangedElements(); // look up the list of elements to highlight and highlight them in the current floater
  167. void highlightChangedFiles(); // look up the list of changed files to highlight and highlight them in the scroll list
  168. void findOverlapsInChildren(LLView* parent); // fill the map below with element overlap information
  169. static BOOL overlapIgnorable(LLView* viewp); // check it the element can be ignored for overlap/localization purposes
  170. // check if two elements overlap using their rectangles
  171. // used instead of llrect functions because by adding a few pixels of leeway I can cut down drastically on the number of overlaps
  172. BOOL elementOverlap(LLView* view1, LLView* view2);
  173. // Button/drop-down action listeners (self explanatory)
  174. void onClickDisplayFloater(S32 id);
  175. void onClickSaveFloater(S32 id);
  176. void onClickSaveAll(S32 id);
  177. void onClickEditFloater();
  178. void onClickBrowseForEditor();
  179. void onClickBrowseForDiffs();
  180. void onClickToggleDiffHighlighting();
  181. void onClickToggleOverlapping();
  182. void onClickCloseDisplayedFloater(S32 id);
  183. void onLanguageComboSelect(LLUICtrl* ctrl);
  184. void onClickExportSchema();
  185. void onClickShowRectangles(const LLSD& data);
  186. };
  187. //----------------------------------------------------------------------------
  188. // Local class declarations
  189. // Reset object to ensure that when we change the current language setting for preview purposes,
  190. // it automatically is reset. Constructed on the stack at the start of the method; the reset
  191. // occurs as it falls out of scope at the end of the method. See llfloateruipreview.cpp for usage.
  192. class LLLocalizationResetForcer
  193. {
  194. public:
  195. LLLocalizationResetForcer(LLFloaterUIPreview* floater, S32 ID);
  196. virtual ~LLLocalizationResetForcer();
  197. private:
  198. std::string mSavedLocalization; // the localization before we change it
  199. };
  200. // Implementation of live file
  201. // When a floater is being previewed, any saved changes to its corresponding
  202. // file cause the previewed floater to be reloaded
  203. class LLGUIPreviewLiveFile : public LLLiveFile
  204. {
  205. public:
  206. LLGUIPreviewLiveFile(std::string path, std::string name, LLFloaterUIPreview* parent);
  207. virtual ~LLGUIPreviewLiveFile();
  208. LLFloaterUIPreview* mParent;
  209. LLFadeEventTimer* mFadeTimer; // timer for fade-to-yellow-and-back effect to warn that file has been reloaded
  210. BOOL mFirstFade; // setting this avoids showing the fade reload warning on first load
  211. std::string mFileName;
  212. protected:
  213. bool loadFile();
  214. };
  215. // Implementation of graphical fade in/out (on timer) for when XUI files are updated
  216. class LLFadeEventTimer : public LLEventTimer
  217. {
  218. public:
  219. LLFadeEventTimer(F32 refresh, LLGUIPreviewLiveFile* parent);
  220. BOOL tick();
  221. LLGUIPreviewLiveFile* mParent;
  222. private:
  223. BOOL mFadingOut; // fades in then out; this is toggled in between
  224. LLColor4 mOriginalColor; // original color; color is reset to this after fade is coimplete
  225. };
  226. // Implementation of previewed floater
  227. // Used to override draw and mouse handler
  228. class LLPreviewedFloater : public LLFloater
  229. {
  230. public:
  231. LLPreviewedFloater(LLFloaterUIPreview* floater, const Params& params)
  232. : LLFloater(LLSD(), params),
  233. mFloaterUIPreview(floater)
  234. {
  235. }
  236. virtual void draw();
  237. BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
  238. BOOL handleToolTip(S32 x, S32 y, MASK mask);
  239. BOOL selectElement(LLView* parent, int x, int y, int depth); // select element to display its overlappers
  240. LLFloaterUIPreview* mFloaterUIPreview;
  241. // draw widget outlines
  242. static bool sShowRectangles;
  243. };
  244. bool LLPreviewedFloater::sShowRectangles = false;
  245. //----------------------------------------------------------------------------
  246. // Localization reset forcer -- ensures that when localization is temporarily changed for previewed floater, it is reset
  247. // Changes are made here
  248. LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater, S32 ID)
  249. {
  250. mSavedLocalization = LLUI::sSettingGroups["config"]->getString("Language"); // save current localization setting
  251. LLUI::sSettingGroups["config"]->setString("Language", floater->getLocStr(ID));// hack language to be the one we want to preview floaters in
  252. LLUI::setupPaths(); // forcibly reset XUI paths with this new language
  253. }
  254. // Actually reset in destructor
  255. // Changes are reversed here
  256. LLLocalizationResetForcer::~LLLocalizationResetForcer()
  257. {
  258. LLUI::sSettingGroups["config"]->setString("Language", mSavedLocalization); // reset language to what it was before we changed it
  259. LLUI::setupPaths(); // forcibly reset XUI paths with this new language
  260. }
  261. // Live file constructor
  262. // Needs full path for LLLiveFile but needs just file name for this code, hence the reduntant arguments; easier than separating later
  263. LLGUIPreviewLiveFile::LLGUIPreviewLiveFile(std::string path, std::string name, LLFloaterUIPreview* parent)
  264. : mFileName(name),
  265. mParent(parent),
  266. mFirstFade(TRUE),
  267. mFadeTimer(NULL),
  268. LLLiveFile(path, 1.0)
  269. {}
  270. LLGUIPreviewLiveFile::~LLGUIPreviewLiveFile()
  271. {
  272. mParent->mLiveFile = NULL;
  273. if(mFadeTimer)
  274. {
  275. mFadeTimer->mParent = NULL;
  276. // deletes itself; see lltimer.cpp
  277. }
  278. }
  279. // Live file load
  280. bool LLGUIPreviewLiveFile::loadFile()
  281. {
  282. mParent->displayFloater(FALSE,1); // redisplay the floater
  283. if(mFirstFade) // only fade if it wasn't just clicked on; can't use "clicked" BOOL below because of an oddity with setting LLLiveFile initial state
  284. {
  285. mFirstFade = FALSE;
  286. }
  287. else
  288. {
  289. if(mFadeTimer)
  290. {
  291. mFadeTimer->mParent = NULL;
  292. }
  293. mFadeTimer = new LLFadeEventTimer(0.05f,this);
  294. }
  295. return true;
  296. }
  297. // Initialize fade event timer
  298. LLFadeEventTimer::LLFadeEventTimer(F32 refresh, LLGUIPreviewLiveFile* parent)
  299. : mParent(parent),
  300. mFadingOut(TRUE),
  301. LLEventTimer(refresh)
  302. {
  303. mOriginalColor = mParent->mParent->mDisplayedFloater->getBackgroundColor();
  304. }
  305. // Single tick of fade event timer: increment the color
  306. BOOL LLFadeEventTimer::tick()
  307. {
  308. float diff = 0.04f;
  309. if(TRUE == mFadingOut) // set fade for in/out color direction
  310. {
  311. diff = -diff;
  312. }
  313. if(NULL == mParent) // no more need to tick, so suicide
  314. {
  315. return TRUE;
  316. }
  317. // Set up colors
  318. LLColor4 bg_color = mParent->mParent->mDisplayedFloater->getBackgroundColor();
  319. LLSD colors = bg_color.getValue();
  320. LLSD colors_old = colors;
  321. // Tick colors
  322. colors[0] = colors[0].asReal() - diff; if(colors[0].asReal() < mOriginalColor.getValue()[0].asReal()) { colors[0] = colors_old[0]; }
  323. colors[1] = colors[1].asReal() - diff; if(colors[1].asReal() < mOriginalColor.getValue()[1].asReal()) { colors[1] = colors_old[1]; }
  324. colors[2] = colors[2].asReal() + diff; if(colors[2].asReal() > mOriginalColor.getValue()[2].asReal()) { colors[2] = colors_old[2]; }
  325. // Clamp and set colors
  326. bg_color.setValue(colors);
  327. bg_color.clamp(); // make sure we didn't exceed [0,1]
  328. mParent->mParent->mDisplayedFloater->setBackgroundColor(bg_color);
  329. if(bg_color[2] <= 0.0f) // end of fade out, start fading in
  330. {
  331. mFadingOut = FALSE;
  332. }
  333. return FALSE;
  334. }
  335. // Constructor
  336. LLFloaterUIPreview::LLFloaterUIPreview(const LLSD& key)
  337. : LLFloater(key),
  338. mDisplayedFloater(NULL),
  339. mDisplayedFloater_2(NULL),
  340. mLiveFile(NULL),
  341. // sHighlightingDiffs(FALSE),
  342. mHighlightingOverlaps(FALSE),
  343. mLastDisplayedX(0),
  344. mLastDisplayedY(0)
  345. {
  346. }
  347. // Destructor
  348. LLFloaterUIPreview::~LLFloaterUIPreview()
  349. {
  350. // spawned floaters are deleted automatically, so we don't need to delete them here
  351. // save contents of textfields so it can be restored later if the floater is created again this session
  352. mSavedEditorPath = mEditorPathTextBox->getText();
  353. mSavedEditorArgs = mEditorArgsTextBox->getText();
  354. mSavedDiffPath = mDiffPathTextBox->getText();
  355. // delete live file if it exists
  356. if(mLiveFile)
  357. {
  358. delete mLiveFile;
  359. mLiveFile = NULL;
  360. }
  361. }
  362. // Perform post-build setup (defined in superclass)
  363. BOOL LLFloaterUIPreview::postBuild()
  364. {
  365. LLPanel* main_panel_tmp = getChild<LLPanel>("main_panel"); // get a pointer to the main panel in order to...
  366. mFileList = main_panel_tmp->getChild<LLScrollListCtrl>("name_list"); // save pointer to file list
  367. // Double-click opens the floater, for convenience
  368. mFileList->setDoubleClickCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, PRIMARY_FLOATER));
  369. setDefaultBtn("display_floater");
  370. // get pointers to buttons and link to callbacks
  371. mLanguageSelection = main_panel_tmp->getChild<LLComboBox>("language_select_combo");
  372. mLanguageSelection->setCommitCallback(boost::bind(&LLFloaterUIPreview::onLanguageComboSelect, this, mLanguageSelection));
  373. mLanguageSelection_2 = main_panel_tmp->getChild<LLComboBox>("language_select_combo_2");
  374. mLanguageSelection_2->setCommitCallback(boost::bind(&LLFloaterUIPreview::onLanguageComboSelect, this, mLanguageSelection));
  375. LLPanel* editor_panel_tmp = main_panel_tmp->getChild<LLPanel>("editor_panel");
  376. mDisplayFloaterBtn = main_panel_tmp->getChild<LLButton>("display_floater");
  377. mDisplayFloaterBtn->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, PRIMARY_FLOATER));
  378. mDisplayFloaterBtn_2 = main_panel_tmp->getChild<LLButton>("display_floater_2");
  379. mDisplayFloaterBtn_2->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, SECONDARY_FLOATER));
  380. mToggleOverlapButton = main_panel_tmp->getChild<LLButton>("toggle_overlap_panel");
  381. mToggleOverlapButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickToggleOverlapping, this));
  382. mCloseOtherButton = main_panel_tmp->getChild<LLButton>("close_displayed_floater");
  383. mCloseOtherButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickCloseDisplayedFloater, this, PRIMARY_FLOATER));
  384. mCloseOtherButton_2 = main_panel_tmp->getChild<LLButton>("close_displayed_floater_2");
  385. mCloseOtherButton_2->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickCloseDisplayedFloater, this, SECONDARY_FLOATER));
  386. mEditFloaterBtn = main_panel_tmp->getChild<LLButton>("edit_floater");
  387. mEditFloaterBtn->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickEditFloater, this));
  388. mExecutableBrowseButton = editor_panel_tmp->getChild<LLButton>("browse_for_executable");
  389. LLPanel* vlt_panel_tmp = main_panel_tmp->getChild<LLPanel>("vlt_panel");
  390. mExecutableBrowseButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickBrowseForEditor, this));
  391. mDiffBrowseButton = vlt_panel_tmp->getChild<LLButton>("browse_for_vlt_diffs");
  392. mDiffBrowseButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickBrowseForDiffs, this));
  393. mToggleHighlightButton = vlt_panel_tmp->getChild<LLButton>("toggle_vlt_diff_highlight");
  394. mToggleHighlightButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickToggleDiffHighlighting, this));
  395. main_panel_tmp->getChild<LLButton>("save_floater")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickSaveFloater, this, PRIMARY_FLOATER));
  396. main_panel_tmp->getChild<LLButton>("save_all_floaters")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickSaveAll, this, PRIMARY_FLOATER));
  397. getChild<LLButton>("export_schema")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickExportSchema, this));
  398. getChild<LLUICtrl>("show_rectangles")->setCommitCallback(
  399. boost::bind(&LLFloaterUIPreview::onClickShowRectangles, this, _2));
  400. // get pointers to text fields
  401. mEditorPathTextBox = editor_panel_tmp->getChild<LLLineEditor>("executable_path_field");
  402. mEditorArgsTextBox = editor_panel_tmp->getChild<LLLineEditor>("executable_args_field");
  403. mDiffPathTextBox = vlt_panel_tmp->getChild<LLLineEditor>("vlt_diff_path_field");
  404. // *HACK: restored saved editor path and args to textfields
  405. mEditorPathTextBox->setText(mSavedEditorPath);
  406. mEditorArgsTextBox->setText(mSavedEditorArgs);
  407. mDiffPathTextBox->setText(mSavedDiffPath);
  408. // Set up overlap panel
  409. mOverlapPanel = getChild<LLOverlapPanel>("overlap_panel");
  410. getChildView("overlap_scroll")->setVisible( mHighlightingOverlaps);
  411. mDelim = gDirUtilp->getDirDelimiter(); // initialize delimiter to dir sep slash
  412. // refresh list of available languages (EN will still be default)
  413. BOOL found = TRUE;
  414. BOOL found_en_us = FALSE;
  415. std::string language_directory;
  416. std::string xui_dir = get_xui_dir(); // directory containing localizations -- don't forget trailing delim
  417. mLanguageSelection->removeall(); // clear out anything temporarily in list from XML
  418. LLDirIterator iter(xui_dir, "*");
  419. while(found) // for every directory
  420. {
  421. if((found = iter.next(language_directory))) // get next directory
  422. {
  423. std::string full_path = xui_dir + language_directory;
  424. if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it
  425. {
  426. continue;
  427. }
  428. if(strncmp("template",language_directory.c_str(),8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory
  429. {
  430. if(!strncmp("en",language_directory.c_str(),5)) // remember if we've seen en, so we can make it default
  431. {
  432. found_en_us = TRUE;
  433. }
  434. else
  435. {
  436. mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu
  437. mLanguageSelection_2->add(std::string(language_directory));
  438. }
  439. }
  440. }
  441. }
  442. if(found_en_us)
  443. {
  444. mLanguageSelection->add(std::string("en"),ADD_TOP); // make en first item if we found it
  445. mLanguageSelection_2->add(std::string("en"),ADD_TOP);
  446. }
  447. else
  448. {
  449. std::string warning = std::string("No EN localization found; check your XUI directories!");
  450. popupAndPrintWarning(warning);
  451. }
  452. mLanguageSelection->selectFirstItem(); // select the first item
  453. mLanguageSelection_2->selectFirstItem();
  454. refreshList(); // refresh the list of available floaters
  455. return TRUE;
  456. }
  457. // Callback for language combo box selection: refresh current floater when you change languages
  458. void LLFloaterUIPreview::onLanguageComboSelect(LLUICtrl* ctrl)
  459. {
  460. LLComboBox* caller = dynamic_cast<LLComboBox*>(ctrl);
  461. if (!caller)
  462. return;
  463. if(caller->getName() == std::string("language_select_combo"))
  464. {
  465. if(mDisplayedFloater)
  466. {
  467. onClickCloseDisplayedFloater(PRIMARY_FLOATER);
  468. displayFloater(TRUE,1);
  469. }
  470. }
  471. else
  472. {
  473. if(mDisplayedFloater_2)
  474. {
  475. onClickCloseDisplayedFloater(PRIMARY_FLOATER);
  476. displayFloater(TRUE,2); // *TODO: make take an arg
  477. }
  478. }
  479. }
  480. void LLFloaterUIPreview::onClickExportSchema()
  481. {
  482. //NOTE: schema generation not complete
  483. //gViewerWindow->setCursor(UI_CURSOR_WAIT);
  484. //std::string template_path = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "xui", "schema");
  485. //typedef LLWidgetTypeRegistry::Registrar::registry_map_t::const_iterator registry_it;
  486. //registry_it end_it = LLWidgetTypeRegistry::defaultRegistrar().endItems();
  487. //for(registry_it it = LLWidgetTypeRegistry::defaultRegistrar().beginItems();
  488. // it != end_it;
  489. // ++it)
  490. //{
  491. // std::string widget_name = it->first;
  492. // const LLInitParam::BaseBlock& block =
  493. // (*LLDefaultParamBlockRegistry::instance().getValue(*LLWidgetTypeRegistry::instance().getValue(widget_name)))();
  494. // LLXMLNodePtr root_nodep = new LLXMLNode();
  495. // LLRNGWriter().writeRNG(widget_name, root_nodep, block, "http://www.lindenlab.com/xui");
  496. // std::string file_name(template_path + gDirUtilp->getDirDelimiter() + widget_name + ".rng");
  497. // LLFILE* rng_file = LLFile::fopen(file_name.c_str(), "w");
  498. // {
  499. // LLXMLNode::writeHeaderToFile(rng_file);
  500. // const bool use_type_decorations = false;
  501. // root_nodep->writeToFile(rng_file, std::string(), use_type_decorations);
  502. // }
  503. // fclose(rng_file);
  504. //}
  505. //gViewerWindow->setCursor(UI_CURSOR_ARROW);
  506. }
  507. void LLFloaterUIPreview::onClickShowRectangles(const LLSD& data)
  508. {
  509. LLPreviewedFloater::sShowRectangles = data.asBoolean();
  510. }
  511. // Close click handler -- delete my displayed floater if it exists
  512. void LLFloaterUIPreview::onClose(bool app_quitting)
  513. {
  514. if(!app_quitting && mDisplayedFloater)
  515. {
  516. onClickCloseDisplayedFloater(PRIMARY_FLOATER);
  517. onClickCloseDisplayedFloater(SECONDARY_FLOATER);
  518. delete mDisplayedFloater;
  519. mDisplayedFloater = NULL;
  520. delete mDisplayedFloater_2;
  521. mDisplayedFloater_2 = NULL;
  522. }
  523. }
  524. // Error handling (to avoid code repetition)
  525. // *TODO: this is currently unlocalized. Add to alerts/notifications.xml, someday, maybe.
  526. void LLFloaterUIPreview::popupAndPrintWarning(const std::string& warning)
  527. {
  528. llwarns << warning << llendl;
  529. LLSD args;
  530. args["MESSAGE"] = warning;
  531. LLNotificationsUtil::add("GenericAlert", args);
  532. }
  533. // Get localization string from drop-down menu
  534. std::string LLFloaterUIPreview::getLocStr(S32 ID)
  535. {
  536. if(ID == 1)
  537. {
  538. return mLanguageSelection->getSelectedItemLabel(0);
  539. }
  540. else
  541. {
  542. return mLanguageSelection_2->getSelectedItemLabel(0);
  543. }
  544. }
  545. // Get localized directory (build path from data directory to XUI files, substituting localization string in for language)
  546. std::string LLFloaterUIPreview::getLocalizedDirectory()
  547. {
  548. return get_xui_dir() + (getLocStr(1)) + mDelim; // e.g. "C:/Code/guipreview/indra/newview/skins/xui/en/";
  549. }
  550. // Refresh the list of floaters by doing a directory traverse for XML XUI floater files
  551. // Could be used to grab any specific language's list of compatible floaters, but currently it's just used to get all of them
  552. void LLFloaterUIPreview::refreshList()
  553. {
  554. // Note: the mask doesn't seem to accept regular expressions, so there need to be two directory searches here
  555. mFileList->clearRows(); // empty list
  556. std::string name;
  557. BOOL found = TRUE;
  558. LLDirIterator floater_iter(getLocalizedDirectory(), "floater_*.xml");
  559. while(found) // for every floater file that matches the pattern
  560. {
  561. if((found = floater_iter.next(name))) // get next file matching pattern
  562. {
  563. addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
  564. }
  565. }
  566. found = TRUE;
  567. LLDirIterator inspect_iter(getLocalizedDirectory(), "inspect_*.xml");
  568. while(found) // for every inspector file that matches the pattern
  569. {
  570. if((found = inspect_iter.next(name))) // get next file matching pattern
  571. {
  572. addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
  573. }
  574. }
  575. found = TRUE;
  576. LLDirIterator menu_iter(getLocalizedDirectory(), "menu_*.xml");
  577. while(found) // for every menu file that matches the pattern
  578. {
  579. if((found = menu_iter.next(name))) // get next file matching pattern
  580. {
  581. addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
  582. }
  583. }
  584. found = TRUE;
  585. LLDirIterator panel_iter(getLocalizedDirectory(), "panel_*.xml");
  586. while(found) // for every panel file that matches the pattern
  587. {
  588. if((found = panel_iter.next(name))) // get next file matching pattern
  589. {
  590. addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
  591. }
  592. }
  593. found = TRUE;
  594. LLDirIterator sidepanel_iter(getLocalizedDirectory(), "sidepanel_*.xml");
  595. while(found) // for every sidepanel file that matches the pattern
  596. {
  597. if((found = sidepanel_iter.next(name))) // get next file matching pattern
  598. {
  599. addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
  600. }
  601. }
  602. if(!mFileList->isEmpty()) // if there were any matching files, just select the first one (so we don't have to worry about disabling buttons when no entry is selected)
  603. {
  604. mFileList->selectFirstItem();
  605. }
  606. }
  607. // Add a single entry to the list of available floaters
  608. // Note: no deduplification (shouldn't be necessary)
  609. void LLFloaterUIPreview::addFloaterEntry(const std::string& path)
  610. {
  611. LLUUID* entry_id = new LLUUID(); // create a new UUID
  612. entry_id->generate(path);
  613. const LLUUID& entry_id_ref = *entry_id; // get a reference to the UUID for the LLSD block
  614. // fill LLSD column entry: initialize row/col structure
  615. LLSD row;
  616. row["id"] = entry_id_ref;
  617. LLSD& columns = row["columns"];
  618. // Get name of floater:
  619. LLXmlTree xml_tree;
  620. std::string full_path = getLocalizedDirectory() + path; // get full path
  621. BOOL success = xml_tree.parseFile(full_path.c_str(), TRUE); // parse xml
  622. std::string entry_name;
  623. std::string entry_title;
  624. if(success)
  625. {
  626. // get root (or error handle)
  627. LLXmlTreeNode* root_floater = xml_tree.getRoot();
  628. if (!root_floater)
  629. {
  630. std::string warning = std::string("No root node found in XUI file: ") + path;
  631. popupAndPrintWarning(warning);
  632. return;
  633. }
  634. // get name
  635. root_floater->getAttributeString("name",entry_name);
  636. if(std::string("") == entry_name)
  637. {
  638. entry_name = "Error: unable to load " + std::string(path); // set to error state if load fails
  639. }
  640. // get title
  641. root_floater->getAttributeString("title",entry_title); // some don't have a title, and some have title = "(unknown)", so just leave it blank if it fails
  642. }
  643. else
  644. {
  645. std::string warning = std::string("Unable to parse XUI file: ") + path; // error handling
  646. popupAndPrintWarning(warning);
  647. if(mLiveFile)
  648. {
  649. delete mLiveFile;
  650. mLiveFile = NULL;
  651. }
  652. return;
  653. }
  654. // Fill floater title column
  655. columns[0]["column"] = "title_column";
  656. columns[0]["type"] = "text";
  657. columns[0]["value"] = entry_title;
  658. // Fill floater path column
  659. columns[1]["column"] = "file_column";
  660. columns[1]["type"] = "text";
  661. columns[1]["value"] = std::string(path);
  662. // Fill floater name column
  663. columns[2]["column"] = "top_level_node_column";
  664. columns[2]["type"] = "text";
  665. columns[2]["value"] = entry_name;
  666. mFileList->addElement(row); // actually add to list
  667. }
  668. // Respond to button click to display/refresh currently-selected floater
  669. void LLFloaterUIPreview::onClickDisplayFloater(S32 caller_id)
  670. {
  671. displayFloater(TRUE, caller_id);
  672. }
  673. // Saves the current floater/panel
  674. void LLFloaterUIPreview::onClickSaveFloater(S32 caller_id)
  675. {
  676. displayFloater(TRUE, caller_id, true);
  677. }
  678. // Saves all floater/panels
  679. void LLFloaterUIPreview::onClickSaveAll(S32 caller_id)
  680. {
  681. int listSize = mFileList->getItemCount();
  682. for (int index = 0; index < listSize; index++)
  683. {
  684. mFileList->selectNthItem(index);
  685. displayFloater(TRUE, caller_id, true);
  686. }
  687. }
  688. // Given path to floater or panel XML file "filename.xml",
  689. // returns "filename_new.xml"
  690. static std::string append_new_to_xml_filename(const std::string& path)
  691. {
  692. std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), path);
  693. std::string::size_type extension_pos = full_filename.rfind(".xml");
  694. full_filename.resize(extension_pos);
  695. full_filename += "_new.xml";
  696. return full_filename;
  697. }
  698. // Actually display the floater
  699. // Only set up a new live file if this came from a click (at which point there should be no existing live file), rather than from the live file's update itself;
  700. // otherwise, we get an infinite loop as the live file keeps recreating itself. That means this function is generally called twice.
  701. void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save)
  702. {
  703. // Convince UI that we're in a different language (the one selected on the drop-down menu)
  704. LLLocalizationResetForcer reset_forcer(this, ID); // save old language in reset forcer object (to be reset upon destruction when it falls out of scope)
  705. LLPreviewedFloater** floaterp = (ID == 1 ? &(mDisplayedFloater) : &(mDisplayedFloater_2));
  706. if(ID == 1)
  707. {
  708. BOOL floater_already_open = mDisplayedFloater != NULL;
  709. if(floater_already_open) // if we are already displaying a floater
  710. {
  711. mLastDisplayedX = mDisplayedFloater->calcScreenRect().mLeft; // save floater's last known position to put the new one there
  712. mLastDisplayedY = mDisplayedFloater->calcScreenRect().mBottom;
  713. delete mDisplayedFloater; // delete it (this closes it too)
  714. mDisplayedFloater = NULL; // and reset the pointer
  715. }
  716. }
  717. else
  718. {
  719. if(mDisplayedFloater_2 != NULL)
  720. {
  721. delete mDisplayedFloater_2;
  722. mDisplayedFloater_2 = NULL;
  723. }
  724. }
  725. std::string path = mFileList->getSelectedItemLabel(1); // get the path of the currently-selected floater
  726. if(std::string("") == path) // if no item is selected
  727. {
  728. return; // ignore click (this can only happen with empty list; otherwise an item is always selected)
  729. }
  730. LLFloater::Params p(LLFloater::getDefaultParams());
  731. p.min_height=p.header_height;
  732. p.min_width=10;
  733. *floaterp = new LLPreviewedFloater(this, p);
  734. if(!strncmp(path.c_str(),"floater_",8)
  735. || !strncmp(path.c_str(), "inspect_", 8)) // if it's a floater
  736. {
  737. if (save)
  738. {
  739. LLXMLNodePtr floater_write = new LLXMLNode();
  740. (*floaterp)->buildFromFile(path, floater_write); // just build it
  741. if (!floater_write->isNull())
  742. {
  743. std::string full_filename = append_new_to_xml_filename(path);
  744. LLFILE* floater_temp = LLFile::fopen(full_filename.c_str(), "w");
  745. LLXMLNode::writeHeaderToFile(floater_temp);
  746. const bool use_type_decorations = false;
  747. floater_write->writeToFile(floater_temp, std::string(), use_type_decorations);
  748. fclose(floater_temp);
  749. }
  750. }
  751. else
  752. {
  753. (*floaterp)->buildFromFile(path); // just build it
  754. (*floaterp)->openFloater((*floaterp)->getKey());
  755. (*floaterp)->setCanResize((*floaterp)->isResizable());
  756. }
  757. }
  758. else if (!strncmp(path.c_str(),"menu_",5)) // if it's a menu
  759. {
  760. if (save)
  761. {
  762. LLXMLNodePtr menu_write = new LLXMLNode();
  763. LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(path, gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance(), menu_write);
  764. if (!menu_write->isNull())
  765. {
  766. std::string full_filename = append_new_to_xml_filename(path);
  767. LLFILE* menu_temp = LLFile::fopen(full_filename.c_str(), "w");
  768. LLXMLNode::writeHeaderToFile(menu_temp);
  769. const bool use_type_decorations = false;
  770. menu_write->writeToFile(menu_temp, std::string(), use_type_decorations);
  771. fclose(menu_temp);
  772. }
  773. delete menu;
  774. }
  775. }
  776. else // if it is a panel...
  777. {
  778. (*floaterp)->setCanResize(true);
  779. const LLFloater::Params& floater_params = LLFloater::getDefaultParams();
  780. S32 floater_header_size = floater_params.header_height;
  781. LLPanel::Params panel_params;
  782. LLPanel* panel = LLUICtrlFactory::create<LLPanel>(panel_params); // create a new panel
  783. if (save)
  784. {
  785. LLXMLNodePtr panel_write = new LLXMLNode();
  786. panel->buildFromFile(path, panel_write); // build it
  787. if (!panel_write->isNull())
  788. {
  789. std::string full_filename = append_new_to_xml_filename(path);
  790. LLFILE* panel_temp = LLFile::fopen(full_filename.c_str(), "w");
  791. LLXMLNode::writeHeaderToFile(panel_temp);
  792. const bool use_type_decorations = false;
  793. panel_write->writeToFile(panel_temp, std::string(), use_type_decorations);
  794. fclose(panel_temp);
  795. }
  796. }
  797. else
  798. {
  799. panel->buildFromFile(path); // build it
  800. LLRect new_size = panel->getRect(); // get its rectangle
  801. panel->setOrigin(2,2); // reset its origin point so it's not offset by -left or other XUI attributes
  802. (*floaterp)->setTitle(path); // use the file name as its title, since panels have no guaranteed meaningful name attribute
  803. panel->setUseBoundingRect(TRUE); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements)
  804. panel->updateBoundingRect(); // update bounding rect
  805. LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect
  806. LLRect new_rect = panel->getRect(); // get the panel's rect
  807. new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible
  808. LLRect floater_rect = new_rect;
  809. floater_rect.stretch(4, 4);
  810. (*floaterp)->reshape(floater_rect.getWidth(), floater_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions
  811. panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed)
  812. (*floaterp)->addChild(panel); // add panel as child
  813. (*floaterp)->openFloater(); // open floater (needed?)
  814. }
  815. }
  816. if(ID == 1)
  817. {
  818. (*floaterp)->setOrigin(mLastDisplayedX, mLastDisplayedY);
  819. }
  820. // *HACK: Remove ability to close it; if you close it, its destructor gets called, but we don't know it's null and try to delete it again,
  821. // resulting in a double free
  822. (*floaterp)->setCanClose(FALSE);
  823. if(ID == 1)
  824. {
  825. mCloseOtherButton->setEnabled(TRUE); // enable my floater's close button
  826. }
  827. else
  828. {
  829. mCloseOtherButton_2->setEnabled(TRUE);
  830. }
  831. // Add localization to title so user knows whether it's localized or defaulted to en
  832. std::string full_path = getLocalizedDirectory() + path;
  833. std::string floater_lang = "EN";
  834. llstat dummy;
  835. if(!LLFile::stat(full_path.c_str(), &dummy)) // if the file does not exist
  836. {
  837. floater_lang = getLocStr(ID);
  838. }
  839. std::string new_title = (*floaterp)->getTitle() + std::string(" [") + floater_lang +
  840. (ID == 1 ? " - Primary" : " - Secondary") + std::string("]");
  841. (*floaterp)->setTitle(new_title);
  842. (*floaterp)->center();
  843. addDependentFloater(*floaterp);
  844. if(click && ID == 1 && !save)
  845. {
  846. // set up live file to track it
  847. if(mLiveFile)
  848. {
  849. delete mLiveFile;
  850. mLiveFile = NULL;
  851. }
  852. mLiveFile = new LLGUIPreviewLiveFile(std::string(full_path.c_str()),std::string(path.c_str()),this);
  853. mLiveFile->checkAndReload();
  854. mLiveFile->addToEventTimer();
  855. }
  856. if(ID == 1)
  857. {
  858. mToggleOverlapButton->setEnabled(TRUE);
  859. }
  860. if(LLView::sHighlightingDiffs && click && ID == 1)
  861. {
  862. highlightChangedElements();
  863. }
  864. if(ID == 1)
  865. {
  866. mOverlapPanel->mOverlapMap.clear();
  867. LLView::sPreviewClickedElement = NULL; // stop overlapping elements from drawing
  868. mOverlapPanel->mLastClickedElement = NULL;
  869. findOverlapsInChildren((LLView*)mDisplayedFloater);
  870. // highlight and enable them
  871. if(mHighlightingOverlaps)
  872. {
  873. for(LLOverlapPanel::OverlapMap::iterator iter = mOverlapPanel->mOverlapMap.begin(); iter != mOverlapPanel->mOverlapMap.end(); ++iter)
  874. {
  875. LLView* viewp = iter->first;
  876. LLView::sPreviewHighlightedElements.insert(viewp);
  877. }
  878. }
  879. else if(LLView::sHighlightingDiffs)
  880. {
  881. highlightChangedElements();
  882. }
  883. }
  884. // NOTE: language is reset here automatically when the reset forcer object falls out of scope (see header for details)
  885. }
  886. // Respond to button click to edit currently-selected floater
  887. void LLFloaterUIPreview::onClickEditFloater()
  888. {
  889. // Determine file to edit.
  890. std::string file_path;
  891. {
  892. std::string file_name = mFileList->getSelectedItemLabel(1); // get the file name of the currently-selected floater
  893. if (file_name.empty()) // if no item is selected
  894. {
  895. llwarns << "No file selected" << llendl;
  896. return; // ignore click
  897. }
  898. file_path = getLocalizedDirectory() + file_name;
  899. // stat file to see if it exists (some localized versions may not have it there are no diffs, and then we try to open an nonexistent file)
  900. llstat dummy;
  901. if(LLFile::stat(file_path.c_str(), &dummy)) // if the file does not exist
  902. {
  903. popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead.");
  904. file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default
  905. }
  906. }
  907. // Set the editor command.
  908. std::string cmd_override;
  909. {
  910. std::string bin = mEditorPathTextBox->getText();
  911. if (!bin.empty())
  912. {
  913. // surround command with double quotes for the case if the path contains spaces
  914. if (bin.find("\"") == std::string::npos)
  915. {
  916. bin = "\"" + bin + "\"";
  917. }
  918. std::string args = mEditorArgsTextBox->getText();
  919. cmd_override = bin + " " + args;
  920. }
  921. }
  922. LLExternalEditor::EErrorCode status = mExternalEditor.setCommand("LL_XUI_EDITOR", cmd_override);
  923. if (status != LLExternalEditor::EC_SUCCESS)
  924. {
  925. std::string warning;
  926. if (status == LLExternalEditor::EC_NOT_SPECIFIED) // Use custom message for this error.
  927. {
  928. warning = getString("ExternalEditorNotSet");
  929. }
  930. else
  931. {
  932. warning = LLExternalEditor::getErrorMessage(status);
  933. }
  934. popupAndPrintWarning(warning);
  935. return;
  936. }
  937. // Run the editor.
  938. if (mExternalEditor.run(file_path) != LLExternalEditor::EC_SUCCESS)
  939. {
  940. popupAndPrintWarning(LLExternalEditor::getErrorMessage(status));
  941. return;
  942. }
  943. }
  944. // Respond to button click to browse for an executable with which to edit XML files
  945. void LLFloaterUIPreview::onClickBrowseForEditor()
  946. {
  947. // create load dialog box
  948. LLFilePicker::ELoadFilter type = (LLFilePicker::ELoadFilter)((intptr_t)((void*)LLFilePicker::FFLOAD_ALL)); // nothing for *.exe so just use all
  949. LLFilePicker& picker = LLFilePicker::instance();
  950. if (!picker.getOpenFile(type)) // user cancelled -- do nothing
  951. {
  952. return;
  953. }
  954. // put the selected path into text field
  955. const std::string chosen_path = picker.getFirstFile();
  956. std::string executable_path = chosen_path;
  957. #if LL_DARWIN
  958. // on Mac, if it's an application bundle, figure out the actual path from the Info.plist file
  959. CFStringRef path_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, chosen_path.c_str(), kCFStringEncodingMacRoman); // get path as a CFStringRef
  960. CFURLRef path_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_cfstr, kCFURLPOSIXPathStyle, TRUE); // turn it into a CFURLRef
  961. CFBundleRef chosen_bundle = CFBundleCreate(kCFAllocatorDefault, path_url); // get a handle for the bundle
  962. if(NULL != chosen_bundle)
  963. {
  964. CFDictionaryRef bundleInfoDict = CFBundleGetInfoDictionary(chosen_bundle); // get the bundle's dictionary
  965. if(NULL != bundleInfoDict)
  966. {
  967. CFStringRef executable_cfstr = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, CFSTR("CFBundleExecutable")); // get the name of the actual executable (e.g. TextEdit or firefox-bin)
  968. int max_file_length = 256; // (max file name length is 255 in OSX)
  969. char executable_buf[max_file_length];
  970. if(CFStringGetCString(executable_cfstr, executable_buf, max_file_length, kCFStringEncodingMacRoman)) // convert CFStringRef to char*
  971. {
  972. executable_path += std::string("/Contents/MacOS/") + std::string(executable_buf); // append path to executable directory and then executable name to exec path
  973. }
  974. else
  975. {
  976. std::string warning = "Unable to get CString from CFString for executable path";
  977. popupAndPrintWarning(warning);
  978. }
  979. }
  980. else
  981. {
  982. std::string warning = "Unable to get bundle info dictionary from application bundle";
  983. popupAndPrintWarning(warning);
  984. }
  985. }
  986. else
  987. {
  988. if(-1 != executable_path.find(".app")) // only warn if this path actually had ".app" in it, i.e. it probably just wasn'nt an app bundle and that's okay
  989. {
  990. std::string warning = std::string("Unable to get bundle from path \"") + chosen_path + std::string("\"");
  991. popupAndPrintWarning(warning);
  992. }
  993. }
  994. #endif
  995. mEditorPathTextBox->setText(std::string(executable_path)); // copy the path to the executable to the textfield for display and later fetching
  996. }
  997. // Respond to button click to browse for a VLT-generated diffs file
  998. void LLFloaterUIPreview::onClickBrowseForDiffs()
  999. {
  1000. // create load dialog box
  1001. LLFilePicker::ELoadFilter type = (LLFilePicker::ELoadFilter)((intptr_t)((void*)LLFilePicker::FFLOAD_XML)); // nothing for *.exe so just use all
  1002. LLFilePicker& picker = LLFilePicker::instance();
  1003. if (!picker.getOpenFile(type)) // user cancelled -- do nothing
  1004. {
  1005. return;
  1006. }
  1007. // put the selected path into text field
  1008. const std::string chosen_path = picker.getFirstFile();
  1009. mDiffPathTextBox->setText(std::string(chosen_path)); // copy the path to the executable to the textfield for display and later fetching
  1010. if(LLView::sHighlightingDiffs) // if we're already highlighting, toggle off and then on so we get the data from the new file
  1011. {
  1012. onClickToggleDiffHighlighting();
  1013. onClickToggleDiffHighlighting();
  1014. }
  1015. }
  1016. void LLFloaterUIPreview::onClickToggleDiffHighlighting()
  1017. {
  1018. if(mHighlightingOverlaps)
  1019. {
  1020. onClickToggleOverlapping();
  1021. mToggleOverlapButton->toggleState();
  1022. }
  1023. LLView::sPreviewHighlightedElements.clear(); // clear lists first
  1024. mDiffsMap.clear();
  1025. mFileList->clearHighlightedItems();
  1026. if(LLView::sHighlightingDiffs) // Turning highlighting off
  1027. {
  1028. LLView::sHighlightingDiffs = !sHighlightingDiffs;
  1029. return;
  1030. }
  1031. else // Turning highlighting on
  1032. {
  1033. // Get the file and make sure it exists
  1034. std::string path_in_textfield = mDiffPathTextBox->getText(); // get file path
  1035. BOOL error = FALSE;
  1036. if(std::string("") == path_in_textfield) // check for blank file
  1037. {
  1038. std::string warning = "Unable to highlight differences because no file was provided; fill in the relevant text field";
  1039. popupAndPrintWarning(warning);
  1040. error = TRUE;
  1041. }
  1042. llstat dummy;
  1043. if(LLFile::stat(path_in_textfield.c_str(), &dummy) && !error) // check if the file exists (empty check is reduntant but useful for the informative error message)
  1044. {
  1045. std::string warning = std::string("Unable to highlight differences because an invalid path to a difference file was provided:\"") + path_in_textfield + "\"";
  1046. popupAndPrintWarning(warning);
  1047. error = TRUE;
  1048. }
  1049. // Build a list of changed elements as given by the XML
  1050. std::list<std::string> changed_element_names;
  1051. LLXmlTree xml_tree;
  1052. BOOL success = xml_tree.parseFile(path_in_textfield.c_str(), TRUE);
  1053. if(success && !error)
  1054. {
  1055. LLXmlTreeNode* root_floater = xml_tree.getRoot();
  1056. if(!strncmp("XuiDelta",root_floater->getName().c_str(),9))
  1057. {
  1058. for (LLXmlTreeNode* child = root_floater->getFirstChild(); // get the first child first, then below get the next one; otherwise the iterator is invalid (bug or feature in XML code?)
  1059. child != NULL;
  1060. child = root_floater->getNextChild()) // get child for next iteration
  1061. {
  1062. if(!strncmp("file",child->getName().c_str(),5))
  1063. {
  1064. scanDiffFile(child);
  1065. }
  1066. else if(!strncmp("error",child->getName().c_str(),6))
  1067. {
  1068. std::string error_file, error_message;
  1069. child->getAttributeString("filename",error_file);
  1070. child->getAttributeString("message",error_message);
  1071. if(mDiffsMap.find(error_file) != mDiffsMap.end())
  1072. {
  1073. mDiffsMap.insert(std::make_pair(error_file,std::make_pair(StringListPtr(new StringList), StringListPtr(new StringList))));
  1074. }
  1075. mDiffsMap[error_file].second->push_back(error_message);
  1076. }
  1077. else
  1078. {
  1079. std::string warning = std::string("Child was neither a file or an error, but rather the following:\"") + std::string(child->getName()) + "\"";
  1080. popupAndPrintWarning(warning);
  1081. error = TRUE;
  1082. break;
  1083. }
  1084. }
  1085. }
  1086. else
  1087. {
  1088. std::string warning = std::string("Root node not named XuiDelta:\"") + path_in_textfield + "\"";
  1089. popupAndPrintWarning(warning);
  1090. error = TRUE;
  1091. }
  1092. }
  1093. else if(!error)
  1094. {
  1095. std::string warning = std::string("Unable to create tree from XML:\"") + path_in_textfield + "\"";
  1096. popupAndPrintWarning(warning);
  1097. error = TRUE;
  1098. }
  1099. if(error) // if we encountered an error, reset the button to off
  1100. {
  1101. mToggleHighlightButton->setToggleState(FALSE);
  1102. }
  1103. else // only toggle if we didn't encounter an error
  1104. {
  1105. LLView::sHighlightingDiffs = !sHighlightingDiffs;
  1106. highlightChangedElements(); // *TODO: this is extraneous, right?
  1107. highlightChangedFiles(); // *TODO: this is extraneous, right?
  1108. }
  1109. }
  1110. }
  1111. void LLFloaterUIPreview::scanDiffFile(LLXmlTreeNode* file_node)
  1112. {
  1113. // Get file name
  1114. std::string file_name;
  1115. file_node->getAttributeString("name",file_name);
  1116. if(std::string("") == file_name)
  1117. {
  1118. std::string warning = std::string("Empty file name encountered in differences:\"") + file_name + "\"";
  1119. popupAndPrintWarning(warning);
  1120. return;
  1121. }
  1122. // Get a list of changed elements
  1123. // Get the first child first, then below get the next one; otherwise the iterator is invalid (bug or feature in XML code?)
  1124. for (LLXmlTreeNode* child = file_node->getFirstChild(); child != NULL; child = file_node->getNextChild())
  1125. {
  1126. if(!strncmp("delta",child->getName().c_str(),6))
  1127. {
  1128. std::string id;
  1129. child->getAttributeString("id",id);
  1130. if(mDiffsMap.find(file_name) == mDiffsMap.end())
  1131. {
  1132. mDiffsMap.insert(std::make_pair(file_name,std::make_pair(StringListPtr(new StringList), StringListPtr(new StringList))));
  1133. }
  1134. mDiffsMap[file_name].first->push_back(std::string(id.c_str()));
  1135. }
  1136. else
  1137. {
  1138. std::string warning = std::string("Child of file was not a delta, but rather the following:\"") + std::string(child->getName()) + "\"";
  1139. popupAndPrintWarning(warning);
  1140. return;
  1141. }
  1142. }
  1143. }
  1144. void LLFloaterUIPreview::highlightChangedElements()
  1145. {
  1146. if(NULL == mLiveFile)
  1147. {
  1148. return;
  1149. }
  1150. // Process differences first (we want their warnings to be shown underneath other warnings)
  1151. StringListPtr changed_element_paths;
  1152. DiffMap::iterator iterExists = mDiffsMap.find(mLiveFile->mFileName);
  1153. if(iterExists != mDiffsMap.end())
  1154. {
  1155. changed_element_paths = mDiffsMap[mLiveFile->mFileName].first; // retrieve list of changed element paths from map
  1156. }
  1157. for(std::list<std::string>::iterator iter = changed_element_paths->begin(); iter != changed_element_paths->end(); ++iter) // for every changed element path
  1158. {
  1159. LLView* element = mDisplayedFloater;
  1160. if(!strncmp(iter->c_str(),".",1)) // if it's the root floater itself
  1161. {
  1162. continue;
  1163. }
  1164. // Split element hierarchy path on period (*HACK: it's possible that the element name will have a period in it, in which case this won't work. See https://wiki.lindenlab.com/wiki/Viewer_Localization_Tool_Documentation.)
  1165. typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
  1166. boost::char_separator<char> sep(".");
  1167. tokenizer tokens(*iter, sep);
  1168. tokenizer::iterator token_iter;
  1169. BOOL failed = FALSE;
  1170. for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
  1171. {
  1172. element = element->findChild<LLView>(*token_iter,FALSE); // try to find element: don't recur, and don't create if missing
  1173. // if we still didn't find it...
  1174. if(NULL == element)
  1175. {
  1176. llinfos << "Unable to find element in XuiDelta file named \"" << *iter << "\" in file \"" << mLiveFile->mFileName <<
  1177. "\". The element may no longer exist, the path may be incorrect, or it may not be a non-displayable element (not an LLView) such as a \"string\" type." << llendl;
  1178. failed = TRUE;
  1179. break;
  1180. }
  1181. }
  1182. if(!failed)
  1183. {
  1184. // Now that we have a pointer to the actual element, add it to the list of elements to be highlighted
  1185. std::set<LLView*>::iterator iter2 = std::find(LLView::sPreviewHighlightedElements.begin(), LLView::sPreviewHighlightedElements.end(), element);
  1186. if(iter2 == LLView::sPreviewHighlightedElements.end())
  1187. {
  1188. LLView::sPreviewHighlightedElements.insert(element);
  1189. }
  1190. }
  1191. }
  1192. // Process errors second, so their warnings show up on top of other warnings
  1193. StringListPtr error_list;
  1194. if(iterExists != mDiffsMap.end())
  1195. {
  1196. error_list = mDiffsMap[mLiveFile->mFileName].second;
  1197. }
  1198. for(std::list<std::string>::iterator iter = error_list->begin(); iter != error_list->end(); ++iter) // for every changed element path
  1199. {
  1200. std::string warning = std::string("Error listed among differences. Filename: \"") + mLiveFile->mFileName + "\". Message: \"" + *iter + "\"";
  1201. popupAndPrintWarning(warning);
  1202. }
  1203. }
  1204. void LLFloaterUIPreview::highlightChangedFiles()
  1205. {
  1206. for(DiffMap::iterator iter = mDiffsMap.begin(); iter != mDiffsMap.end(); ++iter) // for every file listed in diffs
  1207. {
  1208. LLScrollListItem* item = mFileList->getItemByLabel(std::string(iter->first), FALSE, 1);
  1209. if(item)
  1210. {
  1211. item->setHighlighted(TRUE);
  1212. }
  1213. }
  1214. }
  1215. // Respond to button click to browse for an executable with which to edit XML files
  1216. void LLFloaterUIPreview::onClickCloseDisplayedFloater(S32 caller_id)
  1217. {
  1218. if(caller_id == PRIMARY_FLOATER)
  1219. {
  1220. mCloseOtherButton->setEnabled(FALSE);
  1221. mToggleOverlapButton->setEnabled(FALSE);
  1222. if(mDisplayedFloater)
  1223. {
  1224. mLastDisplayedX = mDisplayedFloater->calcScreenRect().mLeft;
  1225. mLastDisplayedY = mDisplayedFloater->calcScreenRect().mBottom;
  1226. delete mDisplayedFloater;
  1227. mDisplayedFloater = NULL;
  1228. }
  1229. if(mLiveFile)
  1230. {
  1231. delete mLiveFile;
  1232. mLiveFile = NULL;
  1233. }
  1234. if(mToggleOverlapButton->getToggleState())
  1235. {
  1236. mToggleOverlapButton->toggleState();
  1237. onClickToggleOverlapping();
  1238. }
  1239. LLView::sPreviewClickedElement = NULL; // stop overlapping elements panel from drawing
  1240. mOverlapPanel->mLastClickedElement = NULL;
  1241. }
  1242. else
  1243. {
  1244. mCloseOtherButton_2->setEnabled(FALSE);
  1245. delete mDisplayedFloater_2;
  1246. mDisplayedFloater_2 = NULL;
  1247. }
  1248. }
  1249. void append_view_tooltip(LLView* tooltip_view, std::string *tooltip_msg)
  1250. {
  1251. LLRect rect = tooltip_view->getRect();
  1252. LLRect parent_rect = tooltip_view->getParent()->getRect();
  1253. S32 left = rect.mLeft;
  1254. // invert coordinate system for XUI top-left layout
  1255. S32 top = parent_rect.getHeight() - rect.mTop;
  1256. if (!tooltip_msg->empty())
  1257. {
  1258. tooltip_msg->append("\n");
  1259. }
  1260. std::string msg = llformat("%s %d, %d (%d x %d)",
  1261. tooltip_view->getName().c_str(),
  1262. left,
  1263. top,
  1264. rect.getWidth(),
  1265. rect.getHeight() );
  1266. tooltip_msg->append( msg );
  1267. }
  1268. BOOL LLPreviewedFloater::handleToolTip(S32 x, S32 y, MASK mask)
  1269. {
  1270. if (!sShowRectangles)
  1271. {
  1272. return LLFloater::handleToolTip(x, y, mask);
  1273. }
  1274. S32 screen_x, screen_y;
  1275. localPointToScreen(x, y, &screen_x, &screen_y);
  1276. std::string tooltip_msg;
  1277. LLView* tooltip_view = this;
  1278. LLView::tree_iterator_t end_it = endTreeDFS();
  1279. for (LLView::tree_iterator_t it = beginTreeDFS(); it != end_it; ++it)
  1280. {
  1281. LLView* viewp = *it;
  1282. LLRect screen_rect;
  1283. viewp->localRectToScreen(viewp->getLocalRect(), &screen_rect);
  1284. if (!(viewp->getVisible()
  1285. && screen_rect.pointInRect(screen_x, screen_y)))
  1286. {
  1287. it.skipDescendants();
  1288. }
  1289. // only report xui names for LLUICtrls, not the various container LLViews
  1290. else if (dynamic_cast<LLUICtrl*>(viewp))
  1291. {
  1292. // if we are in a new part of the tree (not a descendent of current tooltip_view)
  1293. // then push the results for tooltip_view and start with a new potential view
  1294. // NOTE: this emulates visiting only the leaf nodes that meet our criteria
  1295. if (tooltip_view != this
  1296. && !viewp->hasAncestor(tooltip_view))
  1297. {
  1298. append_view_tooltip(tooltip_view, &tooltip_msg);
  1299. }
  1300. tooltip_view = viewp;
  1301. }
  1302. }
  1303. append_view_tooltip(tooltip_view, &tooltip_msg);
  1304. LLToolTipMgr::instance().show(LLToolTip::Params()
  1305. .message(tooltip_msg)
  1306. .max_width(400));
  1307. return TRUE;
  1308. }
  1309. BOOL LLPreviewedFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
  1310. {
  1311. selectElement(this,x,y,0);
  1312. return TRUE;
  1313. }
  1314. // *NOTE: In order to hide all of the overlapping elements of the selected element so as to see it in context, here is what you would need to do:
  1315. // -This selectElement call fills the overlap panel as normal. The element which is "selected" here is actually just an intermediate selection step;
  1316. // what you've really selected is a list of elements: the one you clicked on and everything that overlaps it.
  1317. // -The user then selects one of the elements from this list the overlap panel (click handling to the overlap panel would have to be added).
  1318. // This becomes the final selection (as opposed to the intermediate selection that was just made).
  1319. // -Everything else that is currently displayed on the overlap panel should be hidden from view in the previewed floater itself (setVisible(FALSE)).
  1320. // -Subsequent clicks on other elements in the overlap panel (they should still be there) should make other elements the final selection.
  1321. // -On close or on the click of a new button, everything should be shown again and all selection state should be cleared.
  1322. // ~Jacob, 8/08
  1323. BOOL LLPreviewedFloater::selectElement(LLView* parent, int x, int y, int depth)
  1324. {
  1325. if(getVisible())
  1326. {
  1327. BOOL handled = FALSE;
  1328. if(LLFloaterUIPreview::containerType(parent))
  1329. {
  1330. for(child_list_const_iter_t child_it = parent->getChildList()->begin(); child_it != parent->getChildList()->end(); ++child_it)
  1331. {
  1332. LLView* child = *child_it;
  1333. S32 local_x = x - child->getRect().mLeft;
  1334. S32 local_y = y - child->getRect().mBottom;
  1335. if (child->pointInView(local_x, local_y) &&
  1336. child->getVisible() &&
  1337. selectElement(child, x, y, ++depth))
  1338. {
  1339. handled = TRUE;
  1340. break;
  1341. }
  1342. }
  1343. }
  1344. if(!handled)
  1345. {
  1346. LLView::sPreviewClickedElement = parent;
  1347. }
  1348. return TRUE;
  1349. }
  1350. else
  1351. {
  1352. return FALSE;
  1353. }
  1354. }
  1355. void LLPreviewedFloater::draw()
  1356. {
  1357. if(NULL != mFloaterUIPreview)
  1358. {
  1359. // Set and unset sDrawPreviewHighlights flag so as to avoid using two flags
  1360. if(mFloaterUIPreview->mHighlightingOverlaps)
  1361. {
  1362. LLView::sDrawPreviewHighlights = TRUE;
  1363. }
  1364. // If we're looking for truncations, draw debug rects for the displayed
  1365. // floater only.
  1366. bool old_debug_rects = LLView::sDebugRects;
  1367. bool old_show_names = LLView::sDebugRectsShowNames;
  1368. if (sShowRectangles)
  1369. {
  1370. LLView::sDebugRects = true;
  1371. LLView::sDebugRectsShowNames = false;
  1372. }
  1373. LLFloater::draw();
  1374. LLView::sDebugRects = old_debug_rects;
  1375. LLView::sDebugRectsShowNames = old_show_names;
  1376. if(mFloaterUIPreview->mHighlightingOverlaps)
  1377. {
  1378. LLView::sDrawPreviewHighlights = FALSE;
  1379. }
  1380. }
  1381. }
  1382. void LLFloaterUIPreview::onClickToggleOverlapping()
  1383. {
  1384. if(LLView::sHighlightingDiffs)
  1385. {
  1386. onClickToggleDiffHighlighting();
  1387. mToggleHighlightButton->toggleState();
  1388. }
  1389. LLView::sPreviewHighlightedElements.clear(); // clear lists first
  1390. S32 width, height;
  1391. getResizeLimits(&width, &height); // illegal call of non-static member function
  1392. if(mHighlightingOverlaps)
  1393. {
  1394. mHighlightingOverlaps = !mHighlightingOverlaps;
  1395. // reset list of preview highlighted elements
  1396. setRect(LLRect(getRect().mLeft,getRect().mTop,getRect().mRight - mOverlapPanel->getRect().getWidth(),getRect().mBottom));
  1397. setResizeLimits(width - mOverlapPanel->getRect().getWidth(), height);
  1398. }
  1399. else
  1400. {
  1401. mHighlightingOverlaps = !mHighlightingOverlaps;
  1402. displayFloater(FALSE,1);
  1403. setRect(LLRect(getRect().mLeft,getRect().mTop,getRect().mRight + mOverlapPanel->getRect().getWidth(),getRect().mBottom));
  1404. setResizeLimits(width + mOverlapPanel->getRect().getWidth(), height);
  1405. }
  1406. getChildView("overlap_scroll")->setVisible( mHighlightingOverlaps);
  1407. }
  1408. void LLFloaterUIPreview::findOverlapsInChildren(LLView* parent)
  1409. {
  1410. if(parent->getChildCount() == 0 || !containerType(parent)) // if it has no children or isn't a container type, skip it
  1411. {
  1412. return;
  1413. }
  1414. // for every child of the parent
  1415. for(child_list_const_iter_t child_it = parent->getChildList()->begin(); child_it != parent->getChildList()->end(); ++child_it)
  1416. {
  1417. LLView* child = *child_it;
  1418. if(overlapIgnorable(child))
  1419. {
  1420. continue;
  1421. }
  1422. // for every sibling
  1423. for(child_list_const_iter_t sibling_it = parent->getChildList()->begin(); sibling_it != parent->getChildList()->end(); ++sibling_it) // for each sibling
  1424. {
  1425. LLView* sibling = *sibling_it;
  1426. if(overlapIgnorable(sibling))
  1427. {
  1428. continue;
  1429. }
  1430. // if they overlap... (we don't care if they're visible or enabled -- we want to check those anyway, i.e. hidden tabs that can be later shown)
  1431. if(sibling != child && elementOverlap(child, sibling))
  1432. {
  1433. mOverlapPanel->mOverlapMap[child].push_back(sibling); // add to the map
  1434. }
  1435. }
  1436. findOverlapsInChildren(child); // recur
  1437. }
  1438. }
  1439. // *HACK: don't overlap with the drag handle and various other elements
  1440. // This is using dynamic casts because there is no object-oriented way to tell which elements contain localizable text. These are a few that are ignorable.
  1441. // *NOTE: If a list of elements which have localizable content were created, this function should return false if viewp's class is in that list.
  1442. BOOL LLFloaterUIPreview::overlapIgnorable(LLView* viewp)
  1443. {
  1444. return NULL != dynamic_cast<LLDragHandle*>(viewp) ||
  1445. NULL != dynamic_cast<LLViewBorder*>(viewp) ||
  1446. NULL != dynamic_cast<LLResizeBar*>(viewp);
  1447. }
  1448. // *HACK: these are the only two container types as of 8/08, per Richard
  1449. // This is using dynamic casts because there is no object-oriented way to tell which elements are containers.
  1450. BOOL LLFloaterUIPreview::containerType(LLView* viewp)
  1451. {
  1452. return NULL != dynamic_cast<LLPanel*>(viewp) || NULL != dynamic_cast<LLLayoutStack*>(viewp);
  1453. }
  1454. // Check if two llview's rectangles overlap, with some tolerance
  1455. BOOL LLFloaterUIPreview::elementOverlap(LLView* view1, LLView* view2)
  1456. {
  1457. LLSD rec1 = view1->getRect().getValue();
  1458. LLSD rec2 = view2->getRect().getValue();
  1459. int tolerance = 2;
  1460. return (int)rec1[0] <= (int)rec2[2] - tolerance &&
  1461. (int)rec2[0] <= (int)rec1[2] - tolerance &&
  1462. (int)rec1[3] <= (int)rec2[1] - tolerance &&
  1463. (int)rec2[3] <= (int)rec1[1] - tolerance;
  1464. }
  1465. void LLOverlapPanel::draw()
  1466. {
  1467. static const std::string current_selection_text("Current selection: ");
  1468. static const std::string overlapper_text("Overlapper: ");
  1469. LLColor4 text_color = LLColor4::grey;
  1470. gGL.color4fv(text_color.mV);
  1471. if(!LLView::sPreviewClickedElement)
  1472. {
  1473. LLUI::translate(5,getRect().getHeight()-20); // translate to top-5,left-5
  1474. LLView::sDrawPreviewHighlights = FALSE;
  1475. LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text, 0, 0, 0, text_color,
  1476. LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
  1477. }
  1478. else
  1479. {
  1480. OverlapMap::iterator iterExists = mOverlapMap.find(LLView::sPreviewClickedElement);
  1481. if(iterExists == mOverlapMap.end())
  1482. {
  1483. return;
  1484. }
  1485. std::list<LLView*> overlappers = mOverlapMap[LLView::sPreviewClickedElement];
  1486. if(overlappers.size() == 0)
  1487. {
  1488. LLUI::translate(5,getRect().getHeight()-20); // translate to top-5,left-5
  1489. LLView::sDrawPreviewHighlights = FALSE;
  1490. std::string current_selection = std::string(current_selection_text + LLView::sPreviewClickedElement->getName() + " (no elements overlap)");
  1491. S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(current_selection) + 10;
  1492. LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection, 0, 0, 0, text_color,
  1493. LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
  1494. // widen panel enough to fit this text
  1495. LLRect rect = getRect();
  1496. setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
  1497. return;
  1498. }
  1499. // recalculate required with and height; otherwise use cached
  1500. BOOL need_to_recalculate_bounds = FALSE;
  1501. if(mLastClickedElement == NULL)
  1502. {
  1503. need_to_recalculate_bounds = TRUE;
  1504. }
  1505. if(NULL == mLastClickedElement)
  1506. {
  1507. mLastClickedElement = LLView::sPreviewClickedElement;
  1508. }
  1509. // recalculate bounds for scroll panel
  1510. if(need_to_recalculate_bounds || LLView::sPreviewClickedElement->getName() != mLastClickedElement->getName())
  1511. {
  1512. // reset panel's rectangle to its default width and height (300x600)
  1513. LLRect panel_rect = getRect();
  1514. setRect(LLRect(panel_rect.mLeft,panel_rect.mTop,panel_rect.mLeft+getRect().getWidth(),panel_rect.mTop-getRect().getHeight()));
  1515. LLRect rect;
  1516. // change bounds for selected element
  1517. int height_sum = mLastClickedElement->getRect().getHeight() + mSpacing + 80;
  1518. rect = getRect();
  1519. setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() > mLastClickedElement->getRect().getWidth() + 5 ? rect.mRight : rect.mLeft + mLastClickedElement->getRect().getWidth() + 5, rect.mBottom));
  1520. // and widen to accomodate text if that's wider
  1521. std::string display_text = current_selection_text + LLView::sPreviewClickedElement->getName();
  1522. S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(display_text) + 10;
  1523. rect = getRect();
  1524. setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
  1525. std::list<LLView*> overlappers = mOverlapMap[LLView::sPreviewClickedElement];
  1526. for(std::list<LLView*>::iterator overlap_it = overlappers.begin(); overlap_it != overlappers.end(); ++overlap_it)
  1527. {
  1528. LLView* viewp = *overlap_it;
  1529. height_sum += viewp->getRect().getHeight() + mSpacing*3;
  1530. // widen panel's rectangle to accommodate widest overlapping element of this floater
  1531. rect = getRect();
  1532. setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() > viewp->getRect().getWidth() + 5 ? rect.mRight : rect.mLeft + viewp->getRect().getWidth() + 5, rect.mBottom));
  1533. // and widen to accomodate text if that's wider
  1534. std::string display_text = overlapper_text + viewp->getName();
  1535. S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(display_text) + 10;
  1536. rect = getRect();
  1537. setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
  1538. }
  1539. // change panel's height to accommodate all element heights plus spacing between them
  1540. rect = getRect();
  1541. setRect(LLRect(rect.mLeft,rect.mTop,rect.mRight,rect.mTop-height_sum));
  1542. }
  1543. LLUI::translate(5,getRect().getHeight()-10); // translate to top left
  1544. LLView::sDrawPreviewHighlights = FALSE;
  1545. // draw currently-selected element at top of overlappers
  1546. LLUI::translate(0,-mSpacing);
  1547. LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text + LLView::sPreviewClickedElement->getName(), 0, 0, 0, text_color,
  1548. LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE);
  1549. LLUI::translate(0,-mSpacing-LLView::sPreviewClickedElement->getRect().getHeight()); // skip spacing distance + height
  1550. LLView::sPreviewClickedElement->draw();
  1551. for(std::list<LLView*>::iterator overlap_it = overlappers.begin(); overlap_it != overlappers.end(); ++overlap_it)
  1552. {
  1553. LLView* viewp = *overlap_it;
  1554. // draw separating line
  1555. LLUI::translate(0,-mSpacing);
  1556. gl_line_2d(0,0,getRect().getWidth()-10,0,LLColor4(192.0f/255.0f,192.0f/255.0f,192.0f/255.0f));
  1557. // draw name
  1558. LLUI::translate(0,-mSpacing);
  1559. LLFontGL::getFontSansSerifSmall()->renderUTF8(overlapper_text + viewp->getName(), 0, 0, 0, text_color,
  1560. LLFontGL::L