llfloateruipreview.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:69k
源码类别:

游戏引擎

开发平台:

C++ Builder

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