summaryrefslogtreecommitdiff
path: root/indra/newview/llfloateruipreview.cpp
diff options
context:
space:
mode:
authorAnsariel <ansariel.hiller@phoenixviewer.com>2024-05-22 21:25:21 +0200
committerAndrey Lihatskiy <alihatskiy@productengine.com>2024-05-22 22:40:26 +0300
commite2e37cced861b98de8c1a7c9c0d3a50d2d90e433 (patch)
tree1bb897489ce524986f6196201c10ac0d8861aa5f /indra/newview/llfloateruipreview.cpp
parent069ea06848f766466f1a281144c82a0f2bd79f3a (diff)
Fix line endlings
Diffstat (limited to 'indra/newview/llfloateruipreview.cpp')
-rw-r--r--indra/newview/llfloateruipreview.cpp3434
1 files changed, 1717 insertions, 1717 deletions
diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp
index ba0d5fc2c6..6f526e1905 100644
--- a/indra/newview/llfloateruipreview.cpp
+++ b/indra/newview/llfloateruipreview.cpp
@@ -1,1717 +1,1717 @@
-/**
- * @file llfloateruipreview.cpp
- * @brief Tool for previewing and editing floaters, plus localization tool integration
- *
- * $LicenseInfo:firstyear=2008&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-// Tool for previewing floaters and panels for localization and UI design purposes.
-// See: https://wiki.lindenlab.com/wiki/GUI_Preview_And_Localization_Tools
-// See: https://jira.lindenlab.com/browse/DEV-16869
-
-// *TODO: Translate error messgaes using notifications/alerts.xml
-
-#include "llviewerprecompiledheaders.h" // Precompiled headers
-
-#include "llfloateruipreview.h" // Own header
-
-// Internal utility
-#include "lldiriterator.h"
-#include "lleventtimer.h"
-#include "llexternaleditor.h"
-#include "llrender.h"
-#include "llsdutil.h"
-#include "llxmltree.h"
-#include "llviewerwindow.h"
-
-// XUI
-#include "lluictrlfactory.h"
-#include "llcombobox.h"
-#include "llnotificationsutil.h"
-#include "llresizebar.h"
-#include "llscrolllistitem.h"
-#include "llscrolllistctrl.h"
-#include "llfilepicker.h"
-#include "lldraghandle.h"
-#include "lllayoutstack.h"
-#include "lltooltip.h"
-#include "llviewermenu.h"
-#include "llrngwriter.h"
-#include "llfloater.h" // superclass
-#include "llfloaterreg.h"
-#include "llscrollcontainer.h" // scroll container for overlapping elements
-#include "lllivefile.h" // live file poll/stat/reload
-#include "llviewermenufile.h" // LLFilePickerReplyThread
-
-// Boost (for linux/unix command-line execv)
-#include <boost/tokenizer.hpp>
-#include <boost/shared_ptr.hpp>
-
-// External utility
-#include <string>
-#include <list>
-#include <map>
-
-#if LL_DARWIN
-#include <CoreFoundation/CFURL.h>
-#endif
-
-// Static initialization
-static const S32 PRIMARY_FLOATER = 1;
-static const S32 SECONDARY_FLOATER = 2;
-
-class LLOverlapPanel;
-static LLDefaultChildRegistry::Register<LLOverlapPanel> register_overlap_panel("overlap_panel");
-
-static std::string get_xui_dir()
-{
- std::string delim = gDirUtilp->getDirDelimiter();
- return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim;
-}
-
-// Forward declarations to avoid header dependencies
-class LLColor;
-class LLScrollListCtrl;
-class LLComboBox;
-class LLButton;
-class LLLineEditor;
-class LLXmlTreeNode;
-class LLFloaterUIPreview;
-class LLFadeEventTimer;
-
-class LLLocalizationResetForcer;
-class LLGUIPreviewLiveFile;
-class LLFadeEventTimer;
-class LLPreviewedFloater;
-
-// Implementation of custom overlapping element display panel
-class LLOverlapPanel : public LLPanel
-{
-public:
- struct Params : public LLInitParam::Block<Params, LLPanel::Params>
- {
- Params() {}
- };
- LLOverlapPanel(Params p = Params()) : LLPanel(p),
- mSpacing(10),
- // mClickedElement(NULL),
- mLastClickedElement(NULL)
- {
- mOriginalWidth = getRect().getWidth();
- mOriginalHeight = getRect().getHeight();
- }
- virtual void draw();
-
- typedef std::map<LLView*, std::list<LLView*> > OverlapMap;
- OverlapMap mOverlapMap; // map, of XUI element to a list of XUI elements it overlaps
-
- // LLView *mClickedElement;
- LLView *mLastClickedElement;
- int mOriginalWidth, mOriginalHeight, mSpacing;
-};
-
-
-class LLFloaterUIPreview : public LLFloater
-{
-public:
- // Setup
- LLFloaterUIPreview(const LLSD& key);
- virtual ~LLFloaterUIPreview();
-
- std::string getLocStr(S32 ID); // fetches the localization string based on what is selected in the drop-down menu
- void displayFloater(bool click, S32 ID); // needs to be public so live file can call it when it finds an update
-
- /*virtual*/ bool postBuild();
- /*virtual*/ void onClose(bool app_quitting);
-
- void refreshList(); // refresh list (empty it out and fill it up from scratch)
- void addFloaterEntry(const std::string& path); // add a single file's entry to the list of floaters
-
- static bool containerType(LLView* viewp); // check if the element is a container type and tree traverses need to look at its children
-
-public:
- LLPreviewedFloater* mDisplayedFloater; // the floater which is currently being displayed
- LLPreviewedFloater* mDisplayedFloater_2; // the floater which is currently being displayed
- LLGUIPreviewLiveFile* mLiveFile; // live file for checking for updates to the currently-displayed XML file
- LLOverlapPanel* mOverlapPanel; // custom overlapping elements panel
- // bool mHighlightingDiffs; // bool for whether localization diffs are being highlighted or not
- bool mHighlightingOverlaps; // bool for whether overlapping elements are being highlighted
-
- // 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
- typedef std::list<std::string> StringList;
- typedef std::shared_ptr<StringList> StringListPtr;
- typedef std::map<std::string, std::pair<StringListPtr,StringListPtr> > DiffMap;
- DiffMap mDiffsMap; // map, of filename to pair of list of changed element paths and list of errors
-
-private:
- LLExternalEditor mExternalEditor;
-
- // XUI elements for this floater
- LLScrollListCtrl* mFileList; // scroll list control for file list
- LLLineEditor* mEditorPathTextBox; // text field for path to editor executable
- LLLineEditor* mEditorArgsTextBox; // text field for arguments to editor executable
- LLLineEditor* mDiffPathTextBox; // text field for path to diff file
- LLButton* mDisplayFloaterBtn; // button to display primary floater
- LLButton* mDisplayFloaterBtn_2; // button to display secondary floater
- LLButton* mEditFloaterBtn; // button to edit floater
- LLButton* mExecutableBrowseButton; // button to browse for executable
- LLButton* mCloseOtherButton; // button to close primary displayed floater
- LLButton* mCloseOtherButton_2; // button to close secondary displayed floater
- LLButton* mDiffBrowseButton; // button to browse for diff file
- LLButton* mToggleHighlightButton; // button to toggle highlight of files/elements with diffs
- LLButton* mToggleOverlapButton; // button to togle overlap panel/highlighting
- LLComboBox* mLanguageSelection; // combo box for primary language selection
- LLComboBox* mLanguageSelection_2; // combo box for secondary language selection
- S32 mLastDisplayedX, mLastDisplayedY; // stored position of last floater so the new one opens up in the same place
- std::string mDelim; // the OS-specific delimiter character (/ or \) (*TODO: this shouldn't be needed, right?)
-
- std::string mSavedEditorPath; // stored editor path so closing this floater doesn't reset it
- std::string mSavedEditorArgs; // stored editor args so closing this floater doesn't reset it
- std::string mSavedDiffPath; // stored diff file path so closing this floater doesn't reset it
-
- // Internal functionality
- static void popupAndPrintWarning(const std::string& warning); // pop up a warning
- std::string getLocalizedDirectory(); // build and return the path to the XUI directory for the currently-selected localization
- void scanDiffFile(LLXmlTreeNode* file_node); // scan a given XML node for diff entries and highlight them in its associated file
- void highlightChangedElements(); // look up the list of elements to highlight and highlight them in the current floater
- void highlightChangedFiles(); // look up the list of changed files to highlight and highlight them in the scroll list
- void findOverlapsInChildren(LLView* parent); // fill the map below with element overlap information
- static bool overlapIgnorable(LLView* viewp); // check it the element can be ignored for overlap/localization purposes
-
- // check if two elements overlap using their rectangles
- // used instead of llrect functions because by adding a few pixels of leeway I can cut down drastically on the number of overlaps
- bool elementOverlap(LLView* view1, LLView* view2);
-
- // Button/drop-down action listeners (self explanatory)
- void onClickDisplayFloater(S32 id);
- void onClickSaveFloater(S32 id);
- void onClickSaveAll(S32 id);
- void onClickEditFloater();
- void onClickBrowseForEditor();
- void getExecutablePath(const std::vector<std::string>& filenames);
- void onClickBrowseForDiffs();
- void getDiffsFilePath(const std::vector<std::string>& filenames);
- void onClickToggleDiffHighlighting();
- void onClickToggleOverlapping();
- void onClickCloseDisplayedFloater(S32 id);
- void onLanguageComboSelect(LLUICtrl* ctrl);
- void onClickExportSchema();
- void onClickShowRectangles(const LLSD& data);
-};
-
-//----------------------------------------------------------------------------
-// Local class declarations
-// Reset object to ensure that when we change the current language setting for preview purposes,
-// it automatically is reset. Constructed on the stack at the start of the method; the reset
-// occurs as it falls out of scope at the end of the method. See llfloateruipreview.cpp for usage.
-class LLLocalizationResetForcer
-{
-public:
- LLLocalizationResetForcer(LLFloaterUIPreview* floater, S32 ID);
- virtual ~LLLocalizationResetForcer();
-
-private:
- std::string mSavedLocalization; // the localization before we change it
-};
-
-// Implementation of live file
-// When a floater is being previewed, any saved changes to its corresponding
-// file cause the previewed floater to be reloaded
-class LLGUIPreviewLiveFile : public LLLiveFile
-{
-public:
- LLGUIPreviewLiveFile(std::string path, std::string name, LLFloaterUIPreview* parent);
- virtual ~LLGUIPreviewLiveFile();
- LLFloaterUIPreview* mParent;
- LLFadeEventTimer* mFadeTimer; // timer for fade-to-yellow-and-back effect to warn that file has been reloaded
- bool mFirstFade; // setting this avoids showing the fade reload warning on first load
- std::string mFileName;
-protected:
- bool loadFile();
-};
-
-// Implementation of graphical fade in/out (on timer) for when XUI files are updated
-class LLFadeEventTimer : public LLEventTimer
-{
-public:
- LLFadeEventTimer(F32 refresh, LLGUIPreviewLiveFile* parent);
- bool tick();
- LLGUIPreviewLiveFile* mParent;
-private:
- bool mFadingOut; // fades in then out; this is toggled in between
- LLColor4 mOriginalColor; // original color; color is reset to this after fade is coimplete
-};
-
-// Implementation of previewed floater
-// Used to override draw and mouse handler
-class LLPreviewedFloater : public LLFloater
-{
-public:
- LLPreviewedFloater(LLFloaterUIPreview* floater, const Params& params)
- : LLFloater(LLSD(), params),
- mFloaterUIPreview(floater)
- {
- }
-
- virtual void draw();
- bool handleRightMouseDown(S32 x, S32 y, MASK mask);
- bool handleToolTip(S32 x, S32 y, MASK mask);
- bool selectElement(LLView* parent, int x, int y, int depth); // select element to display its overlappers
-
- LLFloaterUIPreview* mFloaterUIPreview;
-
- // draw widget outlines
- static bool sShowRectangles;
-};
-
-bool LLPreviewedFloater::sShowRectangles = false;
-
-//----------------------------------------------------------------------------
-
-// Localization reset forcer -- ensures that when localization is temporarily changed for previewed floater, it is reset
-// Changes are made here
-LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater, S32 ID)
-{
- mSavedLocalization = LLUI::getInstance()->mSettingGroups["config"]->getString("Language"); // save current localization setting
- LLUI::getInstance()->mSettingGroups["config"]->setString("Language", floater->getLocStr(ID));// hack language to be the one we want to preview floaters in
- // forcibly reset XUI paths with this new language
- gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), floater->getLocStr(ID));
-}
-
-// Actually reset in destructor
-// Changes are reversed here
-LLLocalizationResetForcer::~LLLocalizationResetForcer()
-{
- LLUI::getInstance()->mSettingGroups["config"]->setString("Language", mSavedLocalization); // reset language to what it was before we changed it
- // forcibly reset XUI paths with this new language
- gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), mSavedLocalization);
-}
-
-// Live file constructor
-// Needs full path for LLLiveFile but needs just file name for this code, hence the reduntant arguments; easier than separating later
-LLGUIPreviewLiveFile::LLGUIPreviewLiveFile(std::string path, std::string name, LLFloaterUIPreview* parent)
- : mFileName(name),
- mParent(parent),
- mFirstFade(true),
- mFadeTimer(NULL),
- LLLiveFile(path, 1.0)
-{}
-
-LLGUIPreviewLiveFile::~LLGUIPreviewLiveFile()
-{
- mParent->mLiveFile = NULL;
- if(mFadeTimer)
- {
- mFadeTimer->mParent = NULL;
- // deletes itself; see lltimer.cpp
- }
-}
-
-// Live file load
-bool LLGUIPreviewLiveFile::loadFile()
-{
- mParent->displayFloater(false,1); // redisplay the floater
- 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
- {
- mFirstFade = false;
- }
- else
- {
- if(mFadeTimer)
- {
- mFadeTimer->mParent = NULL;
- }
- mFadeTimer = new LLFadeEventTimer(0.05f,this);
- }
- return true;
-}
-
-// Initialize fade event timer
-LLFadeEventTimer::LLFadeEventTimer(F32 refresh, LLGUIPreviewLiveFile* parent)
- : mParent(parent),
- mFadingOut(true),
- LLEventTimer(refresh)
-{
- mOriginalColor = mParent->mParent->mDisplayedFloater->getBackgroundColor();
-}
-
-// Single tick of fade event timer: increment the color
-bool LLFadeEventTimer::tick()
-{
- float diff = 0.04f;
- if(true == mFadingOut) // set fade for in/out color direction
- {
- diff = -diff;
- }
-
- if(NULL == mParent) // no more need to tick, so suicide
- {
- return true;
- }
-
- // Set up colors
- LLColor4 bg_color = mParent->mParent->mDisplayedFloater->getBackgroundColor();
- LLSD colors = bg_color.getValue();
- LLSD colors_old = colors;
-
- // Tick colors
- colors[0] = colors[0].asReal() - diff; if(colors[0].asReal() < mOriginalColor.getValue()[0].asReal()) { colors[0] = colors_old[0]; }
- colors[1] = colors[1].asReal() - diff; if(colors[1].asReal() < mOriginalColor.getValue()[1].asReal()) { colors[1] = colors_old[1]; }
- colors[2] = colors[2].asReal() + diff; if(colors[2].asReal() > mOriginalColor.getValue()[2].asReal()) { colors[2] = colors_old[2]; }
-
- // Clamp and set colors
- bg_color.setValue(colors);
- bg_color.clamp(); // make sure we didn't exceed [0,1]
- mParent->mParent->mDisplayedFloater->setBackgroundColor(bg_color);
-
- if(bg_color[2] <= 0.0f) // end of fade out, start fading in
- {
- mFadingOut = false;
- }
-
- return false;
-}
-
-// Constructor
-LLFloaterUIPreview::LLFloaterUIPreview(const LLSD& key)
- : LLFloater(key),
- mDisplayedFloater(NULL),
- mDisplayedFloater_2(NULL),
- mLiveFile(NULL),
- // sHighlightingDiffs(false),
- mHighlightingOverlaps(false),
- mLastDisplayedX(0),
- mLastDisplayedY(0)
-{
-}
-
-// Destructor
-LLFloaterUIPreview::~LLFloaterUIPreview()
-{
- // spawned floaters are deleted automatically, so we don't need to delete them here
-
- // save contents of textfields so it can be restored later if the floater is created again this session
- mSavedEditorPath = mEditorPathTextBox->getText();
- mSavedEditorArgs = mEditorArgsTextBox->getText();
- mSavedDiffPath = mDiffPathTextBox->getText();
-
- // delete live file if it exists
- if(mLiveFile)
- {
- delete mLiveFile;
- mLiveFile = NULL;
- }
-}
-
-// Perform post-build setup (defined in superclass)
-bool LLFloaterUIPreview::postBuild()
-{
- LLPanel* main_panel_tmp = getChild<LLPanel>("main_panel"); // get a pointer to the main panel in order to...
- mFileList = main_panel_tmp->getChild<LLScrollListCtrl>("name_list"); // save pointer to file list
- // Double-click opens the floater, for convenience
- mFileList->setDoubleClickCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, PRIMARY_FLOATER));
-
- setDefaultBtn("display_floater");
- // get pointers to buttons and link to callbacks
- mLanguageSelection = main_panel_tmp->getChild<LLComboBox>("language_select_combo");
- mLanguageSelection->setCommitCallback(boost::bind(&LLFloaterUIPreview::onLanguageComboSelect, this, mLanguageSelection));
- mLanguageSelection_2 = main_panel_tmp->getChild<LLComboBox>("language_select_combo_2");
- mLanguageSelection_2->setCommitCallback(boost::bind(&LLFloaterUIPreview::onLanguageComboSelect, this, mLanguageSelection));
- LLPanel* editor_panel_tmp = main_panel_tmp->getChild<LLPanel>("editor_panel");
- mDisplayFloaterBtn = main_panel_tmp->getChild<LLButton>("display_floater");
- mDisplayFloaterBtn->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, PRIMARY_FLOATER));
- mDisplayFloaterBtn_2 = main_panel_tmp->getChild<LLButton>("display_floater_2");
- mDisplayFloaterBtn_2->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, SECONDARY_FLOATER));
- mToggleOverlapButton = main_panel_tmp->getChild<LLButton>("toggle_overlap_panel");
- mToggleOverlapButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickToggleOverlapping, this));
- mCloseOtherButton = main_panel_tmp->getChild<LLButton>("close_displayed_floater");
- mCloseOtherButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickCloseDisplayedFloater, this, PRIMARY_FLOATER));
- mCloseOtherButton_2 = main_panel_tmp->getChild<LLButton>("close_displayed_floater_2");
- mCloseOtherButton_2->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickCloseDisplayedFloater, this, SECONDARY_FLOATER));
- mEditFloaterBtn = main_panel_tmp->getChild<LLButton>("edit_floater");
- mEditFloaterBtn->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickEditFloater, this));
- mExecutableBrowseButton = editor_panel_tmp->getChild<LLButton>("browse_for_executable");
- LLPanel* vlt_panel_tmp = main_panel_tmp->getChild<LLPanel>("vlt_panel");
- mExecutableBrowseButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickBrowseForEditor, this));
- mDiffBrowseButton = vlt_panel_tmp->getChild<LLButton>("browse_for_vlt_diffs");
- mDiffBrowseButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickBrowseForDiffs, this));
- mToggleHighlightButton = vlt_panel_tmp->getChild<LLButton>("toggle_vlt_diff_highlight");
- mToggleHighlightButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickToggleDiffHighlighting, this));
- main_panel_tmp->getChild<LLButton>("save_floater")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickSaveFloater, this, PRIMARY_FLOATER));
- main_panel_tmp->getChild<LLButton>("save_all_floaters")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickSaveAll, this, PRIMARY_FLOATER));
-
- getChild<LLButton>("export_schema")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickExportSchema, this));
- getChild<LLUICtrl>("show_rectangles")->setCommitCallback(
- boost::bind(&LLFloaterUIPreview::onClickShowRectangles, this, _2));
-
- // get pointers to text fields
- mEditorPathTextBox = editor_panel_tmp->getChild<LLLineEditor>("executable_path_field");
- mEditorArgsTextBox = editor_panel_tmp->getChild<LLLineEditor>("executable_args_field");
- mDiffPathTextBox = vlt_panel_tmp->getChild<LLLineEditor>("vlt_diff_path_field");
-
- // *HACK: restored saved editor path and args to textfields
- mEditorPathTextBox->setText(mSavedEditorPath);
- mEditorArgsTextBox->setText(mSavedEditorArgs);
- mDiffPathTextBox->setText(mSavedDiffPath);
-
- // Set up overlap panel
- mOverlapPanel = getChild<LLOverlapPanel>("overlap_panel");
-
- getChildView("overlap_scroll")->setVisible( mHighlightingOverlaps);
-
- mDelim = gDirUtilp->getDirDelimiter(); // initialize delimiter to dir sep slash
-
- // refresh list of available languages (EN will still be default)
- bool found = true;
- bool found_en_us = false;
- std::string language_directory;
- std::string xui_dir = get_xui_dir(); // directory containing localizations -- don't forget trailing delim
- mLanguageSelection->removeall(); // clear out anything temporarily in list from XML
-
- LLDirIterator iter(xui_dir, "*");
- while(found) // for every directory
- {
- if((found = iter.next(language_directory))) // get next directory
- {
- std::string full_path = gDirUtilp->add(xui_dir, language_directory);
- if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it
- {
- continue;
- }
-
- if(strncmp("template",language_directory.c_str(),8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory
- {
- if(!strncmp("en",language_directory.c_str(),5)) // remember if we've seen en, so we can make it default
- {
- found_en_us = true;
- }
- else
- {
- mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu
- mLanguageSelection_2->add(std::string(language_directory));
- }
- }
- }
- }
- if(found_en_us)
- {
- mLanguageSelection->add(std::string("en"),ADD_TOP); // make en first item if we found it
- mLanguageSelection_2->add(std::string("en"),ADD_TOP);
- }
- else
- {
- std::string warning = std::string("No EN localization found; check your XUI directories!");
- popupAndPrintWarning(warning);
- }
- mLanguageSelection->selectFirstItem(); // select the first item
- mLanguageSelection_2->selectFirstItem();
-
- refreshList(); // refresh the list of available floaters
-
- return true;
-}
-
-// Callback for language combo box selection: refresh current floater when you change languages
-void LLFloaterUIPreview::onLanguageComboSelect(LLUICtrl* ctrl)
-{
- LLComboBox* caller = dynamic_cast<LLComboBox*>(ctrl);
- if (!caller)
- return;
- if(caller->getName() == std::string("language_select_combo"))
- {
- if(mDisplayedFloater)
- {
- onClickCloseDisplayedFloater(PRIMARY_FLOATER);
- displayFloater(true,1);
- }
- }
- else
- {
- if(mDisplayedFloater_2)
- {
- onClickCloseDisplayedFloater(PRIMARY_FLOATER);
- displayFloater(true,2); // *TODO: make take an arg
- }
- }
-
-}
-
-void LLFloaterUIPreview::onClickExportSchema()
-{
- //NOTE: schema generation not complete
- //gViewerWindow->setCursor(UI_CURSOR_WAIT);
- //std::string template_path = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "xui", "schema");
-
- //typedef LLWidgetTypeRegistry::Registrar::registry_map_t::const_iterator registry_it;
- //registry_it end_it = LLWidgetTypeRegistry::defaultRegistrar().endItems();
- //for(registry_it it = LLWidgetTypeRegistry::defaultRegistrar().beginItems();
- // it != end_it;
- // ++it)
- //{
- // std::string widget_name = it->first;
- // const LLInitParam::BaseBlock& block =
- // (*LLDefaultParamBlockRegistry::instance().getValue(*LLWidgetTypeRegistry::instance().getValue(widget_name)))();
- // LLXMLNodePtr root_nodep = new LLXMLNode();
- // LLRNGWriter().writeRNG(widget_name, root_nodep, block, "http://www.lindenlab.com/xui");
-
- // std::string file_name(template_path + gDirUtilp->getDirDelimiter() + widget_name + ".rng");
-
- // LLFILE* rng_file = LLFile::fopen(file_name.c_str(), "w");
- // {
- // LLXMLNode::writeHeaderToFile(rng_file);
- // const bool use_type_decorations = false;
- // root_nodep->writeToFile(rng_file, std::string(), use_type_decorations);
- // }
- // fclose(rng_file);
- //}
- //gViewerWindow->setCursor(UI_CURSOR_ARROW);
-}
-
-void LLFloaterUIPreview::onClickShowRectangles(const LLSD& data)
-{
- LLPreviewedFloater::sShowRectangles = data.asBoolean();
-}
-
-// Close click handler -- delete my displayed floater if it exists
-void LLFloaterUIPreview::onClose(bool app_quitting)
-{
- if(!app_quitting && mDisplayedFloater)
- {
- onClickCloseDisplayedFloater(PRIMARY_FLOATER);
- onClickCloseDisplayedFloater(SECONDARY_FLOATER);
- delete mDisplayedFloater;
- mDisplayedFloater = NULL;
- delete mDisplayedFloater_2;
- mDisplayedFloater_2 = NULL;
- }
-}
-
-// Error handling (to avoid code repetition)
-// *TODO: this is currently unlocalized. Add to alerts/notifications.xml, someday, maybe.
-void LLFloaterUIPreview::popupAndPrintWarning(const std::string& warning)
-{
- LL_WARNS() << warning << LL_ENDL;
- LLSD args;
- args["MESSAGE"] = warning;
- LLNotificationsUtil::add("GenericAlert", args);
-}
-
-// Get localization string from drop-down menu
-std::string LLFloaterUIPreview::getLocStr(S32 ID)
-{
- if(ID == 1)
- {
- return mLanguageSelection->getSelectedItemLabel(0);
- }
- else
- {
- return mLanguageSelection_2->getSelectedItemLabel(0);
- }
-}
-
-// Get localized directory (build path from data directory to XUI files, substituting localization string in for language)
-std::string LLFloaterUIPreview::getLocalizedDirectory()
-{
- return get_xui_dir() + (getLocStr(1)) + mDelim; // e.g. "C:/Code/guipreview/indra/newview/skins/xui/en/";
-}
-
-// Refresh the list of floaters by doing a directory traverse for XML XUI floater files
-// Could be used to grab any specific language's list of compatible floaters, but currently it's just used to get all of them
-void LLFloaterUIPreview::refreshList()
-{
- // Note: the mask doesn't seem to accept regular expressions, so there need to be two directory searches here
- mFileList->clearRows(); // empty list
- std::string name;
- bool found = true;
-
- LLDirIterator floater_iter(getLocalizedDirectory(), "floater_*.xml");
- while(found) // for every floater file that matches the pattern
- {
- if((found = floater_iter.next(name))) // get next file matching pattern
- {
- addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
- }
- }
- found = true;
-
- LLDirIterator inspect_iter(getLocalizedDirectory(), "inspect_*.xml");
- while(found) // for every inspector file that matches the pattern
- {
- if((found = inspect_iter.next(name))) // get next file matching pattern
- {
- addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
- }
- }
- found = true;
-
- LLDirIterator menu_iter(getLocalizedDirectory(), "menu_*.xml");
- while(found) // for every menu file that matches the pattern
- {
- if((found = menu_iter.next(name))) // get next file matching pattern
- {
- addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
- }
- }
- found = true;
-
- LLDirIterator panel_iter(getLocalizedDirectory(), "panel_*.xml");
- while(found) // for every panel file that matches the pattern
- {
- if((found = panel_iter.next(name))) // get next file matching pattern
- {
- addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
- }
- }
- found = true;
-
- LLDirIterator sidepanel_iter(getLocalizedDirectory(), "sidepanel_*.xml");
- while(found) // for every sidepanel file that matches the pattern
- {
- if((found = sidepanel_iter.next(name))) // get next file matching pattern
- {
- addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
- }
- }
-
- 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)
- {
- mFileList->selectFirstItem();
- }
-}
-
-// Add a single entry to the list of available floaters
-// Note: no deduplification (shouldn't be necessary)
-void LLFloaterUIPreview::addFloaterEntry(const std::string& path)
-{
- LLUUID* entry_id = new LLUUID(); // create a new UUID
- entry_id->generate(path);
- const LLUUID& entry_id_ref = *entry_id; // get a reference to the UUID for the LLSD block
-
- // fill LLSD column entry: initialize row/col structure
- LLSD row;
- row["id"] = entry_id_ref;
- LLSD& columns = row["columns"];
-
- // Get name of floater:
- LLXmlTree xml_tree;
- std::string full_path = getLocalizedDirectory() + path; // get full path
- bool success = xml_tree.parseFile(full_path.c_str(), true); // parse xml
- std::string entry_name;
- std::string entry_title;
- if(success)
- {
- // get root (or error handle)
- LLXmlTreeNode* root_floater = xml_tree.getRoot();
- if (!root_floater)
- {
- std::string warning = std::string("No root node found in XUI file: ") + path;
- popupAndPrintWarning(warning);
- return;
- }
-
- // get name
- root_floater->getAttributeString("name",entry_name);
- if(std::string("") == entry_name)
- {
- entry_name = "Error: unable to load " + std::string(path); // set to error state if load fails
- }
-
- // get title
- 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
- }
- else
- {
- std::string warning = std::string("Unable to parse XUI file: ") + path; // error handling
- popupAndPrintWarning(warning);
- if(mLiveFile)
- {
- delete mLiveFile;
- mLiveFile = NULL;
- }
- return;
- }
-
- // Fill floater title column
- columns[0]["column"] = "title_column";
- columns[0]["type"] = "text";
- columns[0]["value"] = entry_title;
-
- // Fill floater path column
- columns[1]["column"] = "file_column";
- columns[1]["type"] = "text";
- columns[1]["value"] = std::string(path);
-
- // Fill floater name column
- columns[2]["column"] = "top_level_node_column";
- columns[2]["type"] = "text";
- columns[2]["value"] = entry_name;
-
- mFileList->addElement(row); // actually add to list
-}
-
-// Respond to button click to display/refresh currently-selected floater
-void LLFloaterUIPreview::onClickDisplayFloater(S32 caller_id)
-{
- displayFloater(true, caller_id);
-}
-
-// Saves the current floater/panel
-void LLFloaterUIPreview::onClickSaveFloater(S32 caller_id)
-{
- displayFloater(true, caller_id);
- popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files");
-}
-
-// Saves all floater/panels
-void LLFloaterUIPreview::onClickSaveAll(S32 caller_id)
-{
- int listSize = mFileList->getItemCount();
-
- for (int index = 0; index < listSize; index++)
- {
- mFileList->selectNthItem(index);
- displayFloater(true, caller_id);
- }
- popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files");
-}
-
-// Actually display the floater
-// 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;
-// otherwise, we get an infinite loop as the live file keeps recreating itself. That means this function is generally called twice.
-void LLFloaterUIPreview::displayFloater(bool click, S32 ID)
-{
- // Convince UI that we're in a different language (the one selected on the drop-down menu)
- LLLocalizationResetForcer reset_forcer(this, ID); // save old language in reset forcer object (to be reset upon destruction when it falls out of scope)
-
- LLPreviewedFloater** floaterp = (ID == 1 ? &(mDisplayedFloater) : &(mDisplayedFloater_2));
- if(ID == 1)
- {
- bool floater_already_open = mDisplayedFloater != NULL;
- if(floater_already_open) // if we are already displaying a floater
- {
- mLastDisplayedX = mDisplayedFloater->calcScreenRect().mLeft; // save floater's last known position to put the new one there
- mLastDisplayedY = mDisplayedFloater->calcScreenRect().mBottom;
- delete mDisplayedFloater; // delete it (this closes it too)
- mDisplayedFloater = NULL; // and reset the pointer
- }
- }
- else
- {
- if(mDisplayedFloater_2 != NULL)
- {
- delete mDisplayedFloater_2;
- mDisplayedFloater_2 = NULL;
- }
- }
-
- std::string path = mFileList->getSelectedItemLabel(1); // get the path of the currently-selected floater
- if(std::string("") == path) // if no item is selected
- {
- return; // ignore click (this can only happen with empty list; otherwise an item is always selected)
- }
-
- LLFloater::Params p(LLFloater::getDefaultParams());
- p.min_height=p.header_height;
- p.min_width=10;
-
- *floaterp = new LLPreviewedFloater(this, p);
-
- if(!strncmp(path.c_str(),"floater_",8)
- || !strncmp(path.c_str(), "inspect_", 8)) // if it's a floater
- {
- (*floaterp)->buildFromFile(path); // just build it
- (*floaterp)->openFloater((*floaterp)->getKey());
- (*floaterp)->setCanResize((*floaterp)->isResizable());
- }
- else if (!strncmp(path.c_str(),"menu_",5)) // if it's a menu
- {
- // former 'save' processing excised
- }
- else // if it is a panel...
- {
- (*floaterp)->setCanResize(true);
-
- const LLFloater::Params& floater_params = LLFloater::getDefaultParams();
- S32 floater_header_size = floater_params.header_height;
-
- LLPanel::Params panel_params;
- LLPanel* panel = LLUICtrlFactory::create<LLPanel>(panel_params); // create a new panel
-
- panel->buildFromFile(path); // build it
- panel->setOrigin(2,2); // reset its origin point so it's not offset by -left or other XUI attributes
- (*floaterp)->setTitle(path); // use the file name as its title, since panels have no guaranteed meaningful name attribute
- panel->setUseBoundingRect(true); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements)
- panel->updateBoundingRect(); // update bounding rect
- LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect
- LLRect new_rect = panel->getRect(); // get the panel's rect
- new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible
- LLRect floater_rect = new_rect;
- floater_rect.stretch(4, 4);
- (*floaterp)->reshape(floater_rect.getWidth(), floater_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions
- panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed)
- (*floaterp)->addChild(panel); // add panel as child
- (*floaterp)->openFloater(); // open floater (needed?)
- }
-
- if(ID == 1)
- {
- (*floaterp)->setOrigin(mLastDisplayedX, mLastDisplayedY);
- }
-
- // *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,
- // resulting in a double free
- (*floaterp)->setCanClose(false);
-
- if(ID == 1)
- {
- mCloseOtherButton->setEnabled(true); // enable my floater's close button
- }
- else
- {
- mCloseOtherButton_2->setEnabled(true);
- }
-
- // Add localization to title so user knows whether it's localized or defaulted to en
- std::string full_path = getLocalizedDirectory() + path;
- std::string floater_lang = "EN";
- llstat dummy;
- if(!LLFile::stat(full_path.c_str(), &dummy)) // if the file does not exist
- {
- floater_lang = getLocStr(ID);
- }
- std::string new_title = (*floaterp)->getTitle() + std::string(" [") + floater_lang +
- (ID == 1 ? " - Primary" : " - Secondary") + std::string("]");
- (*floaterp)->setTitle(new_title);
-
- (*floaterp)->center();
- addDependentFloater(*floaterp);
-
- if(click && ID == 1)
- {
- // set up live file to track it
- if(mLiveFile)
- {
- delete mLiveFile;
- mLiveFile = NULL;
- }
- mLiveFile = new LLGUIPreviewLiveFile(std::string(full_path.c_str()),std::string(path.c_str()),this);
- mLiveFile->checkAndReload();
- mLiveFile->addToEventTimer();
- }
-
- if(ID == 1)
- {
- mToggleOverlapButton->setEnabled(true);
- }
-
- if(LLView::sHighlightingDiffs && click && ID == 1)
- {
- highlightChangedElements();
- }
-
- if(ID == 1)
- {
- mOverlapPanel->mOverlapMap.clear();
- LLView::sPreviewClickedElement = NULL; // stop overlapping elements from drawing
- mOverlapPanel->mLastClickedElement = NULL;
- findOverlapsInChildren((LLView*)mDisplayedFloater);
-
- // highlight and enable them
- if(mHighlightingOverlaps)
- {
- for(LLOverlapPanel::OverlapMap::iterator iter = mOverlapPanel->mOverlapMap.begin(); iter != mOverlapPanel->mOverlapMap.end(); ++iter)
- {
- LLView* viewp = iter->first;
- LLView::sPreviewHighlightedElements.insert(viewp);
- }
- }
- else if(LLView::sHighlightingDiffs)
- {
- highlightChangedElements();
- }
- }
-
- // NOTE: language is reset here automatically when the reset forcer object falls out of scope (see header for details)
-}
-
-// Respond to button click to edit currently-selected floater
-void LLFloaterUIPreview::onClickEditFloater()
-{
- // Determine file to edit.
- std::string file_path;
- {
- std::string file_name = mFileList->getSelectedItemLabel(1); // get the file name of the currently-selected floater
- if (file_name.empty()) // if no item is selected
- {
- LL_WARNS() << "No file selected" << LL_ENDL;
- return; // ignore click
- }
- file_path = getLocalizedDirectory() + file_name;
-
- // 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)
- llstat dummy;
- if(LLFile::stat(file_path.c_str(), &dummy)) // if the file does not exist
- {
- popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead.");
- file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default
- }
- }
-
- // Set the editor command.
- std::string cmd_override;
- {
- std::string bin = mEditorPathTextBox->getText();
- if (!bin.empty())
- {
- // surround command with double quotes for the case if the path contains spaces
- if (bin.find("\"") == std::string::npos)
- {
- bin = "\"" + bin + "\"";
- }
-
- std::string args = mEditorArgsTextBox->getText();
- cmd_override = bin + " " + args;
- }
- }
-
- LLExternalEditor::EErrorCode status = mExternalEditor.setCommand("LL_XUI_EDITOR", cmd_override);
- if (status != LLExternalEditor::EC_SUCCESS)
- {
- std::string warning;
-
- if (status == LLExternalEditor::EC_NOT_SPECIFIED) // Use custom message for this error.
- {
- warning = getString("ExternalEditorNotSet");
- }
- else
- {
- warning = LLExternalEditor::getErrorMessage(status);
- }
-
- popupAndPrintWarning(warning);
- return;
- }
-
- // Run the editor.
- if (mExternalEditor.run(file_path) != LLExternalEditor::EC_SUCCESS)
- {
- popupAndPrintWarning(LLExternalEditor::getErrorMessage(status));
- return;
- }
-}
-
-// Respond to button click to browse for an executable with which to edit XML files
-void LLFloaterUIPreview::onClickBrowseForEditor()
-{
- // Let the user choose an executable through the file picker dialog box
- LLFilePickerReplyThread::startPicker(boost::bind(&LLFloaterUIPreview::getExecutablePath, this, _1), LLFilePicker::FFLOAD_EXE, false);
-}
-
-void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filenames)
-{
- // put the selected path into text field
- const std::string chosen_path = filenames[0];
- std::string executable_path = chosen_path;
-#if LL_DARWIN
- // on Mac, if it's an application bundle, figure out the actual path from the Info.plist file
- CFStringRef path_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, chosen_path.c_str(), kCFStringEncodingMacRoman); // get path as a CFStringRef
- CFURLRef path_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_cfstr, kCFURLPOSIXPathStyle, true); // turn it into a CFURLRef
- CFBundleRef chosen_bundle = CFBundleCreate(kCFAllocatorDefault, path_url); // get a handle for the bundle
- if(NULL != chosen_bundle)
- {
- CFDictionaryRef bundleInfoDict = CFBundleGetInfoDictionary(chosen_bundle); // get the bundle's dictionary
- if(NULL != bundleInfoDict)
- {
- CFStringRef executable_cfstr = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, CFSTR("CFBundleExecutable")); // get the name of the actual executable (e.g. TextEdit or firefox-bin)
- int max_file_length = 256; // (max file name length is 255 in OSX)
- char executable_buf[max_file_length];
- if(CFStringGetCString(executable_cfstr, executable_buf, max_file_length, kCFStringEncodingMacRoman)) // convert CFStringRef to char*
- {
- executable_path += std::string("/Contents/MacOS/") + std::string(executable_buf); // append path to executable directory and then executable name to exec path
- }
- else
- {
- std::string warning = "Unable to get CString from CFString for executable path";
- popupAndPrintWarning(warning);
- }
- }
- else
- {
- std::string warning = "Unable to get bundle info dictionary from application bundle";
- popupAndPrintWarning(warning);
- }
- }
- else
- {
- 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
- {
- std::string warning = std::string("Unable to get bundle from path \"") + chosen_path + std::string("\"");
- popupAndPrintWarning(warning);
- }
- }
-
-#endif
- mEditorPathTextBox->setText(std::string(executable_path)); // copy the path to the executable to the textfield for display and later fetching
-}
-
-// Respond to button click to browse for a VLT-generated diffs file
-void LLFloaterUIPreview::onClickBrowseForDiffs()
-{
- // create load dialog box
- LLFilePickerReplyThread::startPicker(boost::bind(&LLFloaterUIPreview::getDiffsFilePath, this, _1), LLFilePicker::FFLOAD_XML, false);
-}
-
-void LLFloaterUIPreview::getDiffsFilePath(const std::vector<std::string>& filenames)
-{
- // put the selected path into text field
- const std::string chosen_path = filenames[0];
- mDiffPathTextBox->setText(std::string(chosen_path)); // copy the path to the executable to the textfield for display and later fetching
- if(LLView::sHighlightingDiffs) // if we're already highlighting, toggle off and then on so we get the data from the new file
- {
- onClickToggleDiffHighlighting();
- onClickToggleDiffHighlighting();
- }
-}
-
-void LLFloaterUIPreview::onClickToggleDiffHighlighting()
-{
- if(mHighlightingOverlaps)
- {
- onClickToggleOverlapping();
- mToggleOverlapButton->toggleState();
- }
-
- LLView::sPreviewHighlightedElements.clear(); // clear lists first
- mDiffsMap.clear();
- mFileList->clearHighlightedItems();
-
- if(LLView::sHighlightingDiffs) // Turning highlighting off
- {
- LLView::sHighlightingDiffs = !sHighlightingDiffs;
- return;
- }
- else // Turning highlighting on
- {
- // Get the file and make sure it exists
- std::string path_in_textfield = mDiffPathTextBox->getText(); // get file path
- bool error = false;
-
- if(std::string("") == path_in_textfield) // check for blank file
- {
- std::string warning = "Unable to highlight differences because no file was provided; fill in the relevant text field";
- popupAndPrintWarning(warning);
- error = true;
- }
-
- llstat dummy;
- 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)
- {
- std::string warning = std::string("Unable to highlight differences because an invalid path to a difference file was provided:\"") + path_in_textfield + "\"";
- popupAndPrintWarning(warning);
- error = true;
- }
-
- // Build a list of changed elements as given by the XML
- std::list<std::string> changed_element_names;
- LLXmlTree xml_tree;
- bool success = xml_tree.parseFile(path_in_textfield.c_str(), true);
-
- if(success && !error)
- {
- LLXmlTreeNode* root_floater = xml_tree.getRoot();
- if(!strncmp("XuiDelta",root_floater->getName().c_str(),9))
- {
- 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?)
- child != NULL;
- child = root_floater->getNextChild()) // get child for next iteration
- {
- if(!strncmp("file",child->getName().c_str(),5))
- {
- scanDiffFile(child);
- }
- else if(!strncmp("error",child->getName().c_str(),6))
- {
- std::string error_file, error_message;
- child->getAttributeString("filename",error_file);
- child->getAttributeString("message",error_message);
- if(mDiffsMap.find(error_file) != mDiffsMap.end())
- {
- mDiffsMap.insert(std::make_pair(error_file,std::make_pair(StringListPtr(new StringList), StringListPtr(new StringList))));
- }
- mDiffsMap[error_file].second->push_back(error_message);
- }
- else
- {
- std::string warning = std::string("Child was neither a file or an error, but rather the following:\"") + std::string(child->getName()) + "\"";
- popupAndPrintWarning(warning);
- error = true;
- break;
- }
- }
- }
- else
- {
- std::string warning = std::string("Root node not named XuiDelta:\"") + path_in_textfield + "\"";
- popupAndPrintWarning(warning);
- error = true;
- }
- }
- else if(!error)
- {
- std::string warning = std::string("Unable to create tree from XML:\"") + path_in_textfield + "\"";
- popupAndPrintWarning(warning);
- error = true;
- }
-
- if(error) // if we encountered an error, reset the button to off
- {
- mToggleHighlightButton->setToggleState(false);
- }
- else // only toggle if we didn't encounter an error
- {
- LLView::sHighlightingDiffs = !sHighlightingDiffs;
- highlightChangedElements(); // *TODO: this is extraneous, right?
- highlightChangedFiles(); // *TODO: this is extraneous, right?
- }
- }
-}
-
-void LLFloaterUIPreview::scanDiffFile(LLXmlTreeNode* file_node)
-{
- // Get file name
- std::string file_name;
- file_node->getAttributeString("name",file_name);
- if(std::string("") == file_name)
- {
- std::string warning = std::string("Empty file name encountered in differences:\"") + file_name + "\"";
- popupAndPrintWarning(warning);
- return;
- }
-
- // Get a list of changed elements
- // Get the first child first, then below get the next one; otherwise the iterator is invalid (bug or feature in XML code?)
- for (LLXmlTreeNode* child = file_node->getFirstChild(); child != NULL; child = file_node->getNextChild())
- {
- if(!strncmp("delta",child->getName().c_str(),6))
- {
- std::string id;
- child->getAttributeString("id",id);
- if(mDiffsMap.find(file_name) == mDiffsMap.end())
- {
- mDiffsMap.insert(std::make_pair(file_name,std::make_pair(StringListPtr(new StringList), StringListPtr(new StringList))));
- }
- mDiffsMap[file_name].first->push_back(std::string(id.c_str()));
- }
- else
- {
- std::string warning = std::string("Child of file was not a delta, but rather the following:\"") + std::string(child->getName()) + "\"";
- popupAndPrintWarning(warning);
- return;
- }
- }
-}
-
-void LLFloaterUIPreview::highlightChangedElements()
-{
- if(NULL == mLiveFile)
- {
- return;
- }
-
- // Process differences first (we want their warnings to be shown underneath other warnings)
- StringListPtr changed_element_paths;
- DiffMap::iterator iterExists = mDiffsMap.find(mLiveFile->mFileName);
- if(iterExists != mDiffsMap.end())
- {
- changed_element_paths = mDiffsMap[mLiveFile->mFileName].first; // retrieve list of changed element paths from map
- }
-
- for(std::list<std::string>::iterator iter = changed_element_paths->begin(); iter != changed_element_paths->end(); ++iter) // for every changed element path
- {
- LLView* element = mDisplayedFloater;
- if(!strncmp(iter->c_str(),".",1)) // if it's the root floater itself
- {
- continue;
- }
-
- // 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.)
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(".");
- tokenizer tokens(*iter, sep);
- tokenizer::iterator token_iter;
- bool failed = false;
- for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
- {
- element = element->findChild<LLView>(*token_iter,false); // try to find element: don't recur, and don't create if missing
-
- // if we still didn't find it...
- if(NULL == element)
- {
- LL_INFOS() << "Unable to find element in XuiDelta file named \"" << *iter << "\" in file \"" << mLiveFile->mFileName <<
- "\". 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." << LL_ENDL;
- failed = true;
- break;
- }
- }
-
- if(!failed)
- {
- // Now that we have a pointer to the actual element, add it to the list of elements to be highlighted
- std::set<LLView*>::iterator iter2 = std::find(LLView::sPreviewHighlightedElements.begin(), LLView::sPreviewHighlightedElements.end(), element);
- if(iter2 == LLView::sPreviewHighlightedElements.end())
- {
- LLView::sPreviewHighlightedElements.insert(element);
- }
- }
- }
-
- // Process errors second, so their warnings show up on top of other warnings
- StringListPtr error_list;
- if(iterExists != mDiffsMap.end())
- {
- error_list = mDiffsMap[mLiveFile->mFileName].second;
- }
- for(std::list<std::string>::iterator iter = error_list->begin(); iter != error_list->end(); ++iter) // for every changed element path
- {
- std::string warning = std::string("Error listed among differences. Filename: \"") + mLiveFile->mFileName + "\". Message: \"" + *iter + "\"";
- popupAndPrintWarning(warning);
- }
-}
-
-void LLFloaterUIPreview::highlightChangedFiles()
-{
- for(DiffMap::iterator iter = mDiffsMap.begin(); iter != mDiffsMap.end(); ++iter) // for every file listed in diffs
- {
- LLScrollListItem* item = mFileList->getItemByLabel(std::string(iter->first), false, 1);
- if(item)
- {
- item->setHighlighted(true);
- }
- }
-}
-
-// Respond to button click to browse for an executable with which to edit XML files
-void LLFloaterUIPreview::onClickCloseDisplayedFloater(S32 caller_id)
-{
- if(caller_id == PRIMARY_FLOATER)
- {
- mCloseOtherButton->setEnabled(false);
- mToggleOverlapButton->setEnabled(false);
-
- if(mDisplayedFloater)
- {
- mLastDisplayedX = mDisplayedFloater->calcScreenRect().mLeft;
- mLastDisplayedY = mDisplayedFloater->calcScreenRect().mBottom;
- delete mDisplayedFloater;
- mDisplayedFloater = NULL;
- }
-
- if(mLiveFile)
- {
- delete mLiveFile;
- mLiveFile = NULL;
- }
-
- if(mToggleOverlapButton->getToggleState())
- {
- mToggleOverlapButton->toggleState();
- onClickToggleOverlapping();
- }
-
- LLView::sPreviewClickedElement = NULL; // stop overlapping elements panel from drawing
- mOverlapPanel->mLastClickedElement = NULL;
- }
- else
- {
- mCloseOtherButton_2->setEnabled(false);
- delete mDisplayedFloater_2;
- mDisplayedFloater_2 = NULL;
- }
-
-}
-
-void append_view_tooltip(LLView* tooltip_view, std::string *tooltip_msg)
-{
- LLRect rect = tooltip_view->getRect();
- LLRect parent_rect = tooltip_view->getParent()->getRect();
- S32 left = rect.mLeft;
- // invert coordinate system for XUI top-left layout
- S32 top = parent_rect.getHeight() - rect.mTop;
- if (!tooltip_msg->empty())
- {
- tooltip_msg->append("\n");
- }
- std::string msg = llformat("%s %d, %d (%d x %d)",
- tooltip_view->getName().c_str(),
- left,
- top,
- rect.getWidth(),
- rect.getHeight() );
- tooltip_msg->append( msg );
-}
-
-bool LLPreviewedFloater::handleToolTip(S32 x, S32 y, MASK mask)
-{
- if (!sShowRectangles)
- {
- return LLFloater::handleToolTip(x, y, mask);
- }
-
- S32 screen_x, screen_y;
- localPointToScreen(x, y, &screen_x, &screen_y);
- std::string tooltip_msg;
- LLView* tooltip_view = this;
- LLView::tree_iterator_t end_it = endTreeDFS();
- for (LLView::tree_iterator_t it = beginTreeDFS(); it != end_it; ++it)
- {
- LLView* viewp = *it;
- LLRect screen_rect;
- viewp->localRectToScreen(viewp->getLocalRect(), &screen_rect);
- if (!(viewp->getVisible()
- && screen_rect.pointInRect(screen_x, screen_y)))
- {
- it.skipDescendants();
- }
- // only report xui names for LLUICtrls, not the various container LLViews
-
- else if (dynamic_cast<LLUICtrl*>(viewp))
- {
- // if we are in a new part of the tree (not a descendent of current tooltip_view)
- // then push the results for tooltip_view and start with a new potential view
- // NOTE: this emulates visiting only the leaf nodes that meet our criteria
-
- if (tooltip_view != this
- && !viewp->hasAncestor(tooltip_view))
- {
- append_view_tooltip(tooltip_view, &tooltip_msg);
- }
- tooltip_view = viewp;
- }
- }
-
- append_view_tooltip(tooltip_view, &tooltip_msg);
-
- LLToolTipMgr::instance().show(LLToolTip::Params()
- .message(tooltip_msg)
- .max_width(400));
- return true;
-}
-
-bool LLPreviewedFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
-{
- selectElement(this,x,y,0);
- return true;
-}
-
-// *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:
-// -This selectElement call fills the overlap panel as normal. The element which is "selected" here is actually just an intermediate selection step;
-// what you've really selected is a list of elements: the one you clicked on and everything that overlaps it.
-// -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).
-// This becomes the final selection (as opposed to the intermediate selection that was just made).
-// -Everything else that is currently displayed on the overlap panel should be hidden from view in the previewed floater itself (setVisible(false)).
-// -Subsequent clicks on other elements in the overlap panel (they should still be there) should make other elements the final selection.
-// -On close or on the click of a new button, everything should be shown again and all selection state should be cleared.
-// ~Jacob, 8/08
-bool LLPreviewedFloater::selectElement(LLView* parent, int x, int y, int depth)
-{
- if(getVisible())
- {
- bool handled = false;
- if(LLFloaterUIPreview::containerType(parent))
- {
- for(child_list_const_iter_t child_it = parent->getChildList()->begin(); child_it != parent->getChildList()->end(); ++child_it)
- {
- LLView* child = *child_it;
- S32 local_x = x - child->getRect().mLeft;
- S32 local_y = y - child->getRect().mBottom;
- if (child->pointInView(local_x, local_y) &&
- child->getVisible() &&
- selectElement(child, x, y, ++depth))
- {
- handled = true;
- break;
- }
- }
- }
-
- if(!handled)
- {
- LLView::sPreviewClickedElement = parent;
- }
- return true;
- }
- else
- {
- return false;
- }
-}
-
-void LLPreviewedFloater::draw()
-{
- if(NULL != mFloaterUIPreview)
- {
- // Set and unset sDrawPreviewHighlights flag so as to avoid using two flags
- if(mFloaterUIPreview->mHighlightingOverlaps)
- {
- LLView::sDrawPreviewHighlights = true;
- }
-
- // If we're looking for truncations, draw debug rects for the displayed
- // floater only.
- bool old_debug_rects = LLView::sDebugRects;
- bool old_show_names = LLView::sDebugRectsShowNames;
- if (sShowRectangles)
- {
- LLView::sDebugRects = true;
- LLView::sDebugRectsShowNames = false;
- }
-
- LLFloater::draw();
-
- LLView::sDebugRects = old_debug_rects;
- LLView::sDebugRectsShowNames = old_show_names;
-
- if(mFloaterUIPreview->mHighlightingOverlaps)
- {
- LLView::sDrawPreviewHighlights = false;
- }
- }
-}
-
-void LLFloaterUIPreview::onClickToggleOverlapping()
-{
- if(LLView::sHighlightingDiffs)
- {
- onClickToggleDiffHighlighting();
- mToggleHighlightButton->toggleState();
- }
- LLView::sPreviewHighlightedElements.clear(); // clear lists first
-
- S32 width, height;
- getResizeLimits(&width, &height); // illegal call of non-static member function
- if(mHighlightingOverlaps)
- {
- mHighlightingOverlaps = !mHighlightingOverlaps;
- // reset list of preview highlighted elements
- setRect(LLRect(getRect().mLeft,getRect().mTop,getRect().mRight - mOverlapPanel->getRect().getWidth(),getRect().mBottom));
- setResizeLimits(width - mOverlapPanel->getRect().getWidth(), height);
- }
- else
- {
- mHighlightingOverlaps = !mHighlightingOverlaps;
- displayFloater(false,1);
- setRect(LLRect(getRect().mLeft,getRect().mTop,getRect().mRight + mOverlapPanel->getRect().getWidth(),getRect().mBottom));
- setResizeLimits(width + mOverlapPanel->getRect().getWidth(), height);
- }
- getChildView("overlap_scroll")->setVisible( mHighlightingOverlaps);
-}
-
-void LLFloaterUIPreview::findOverlapsInChildren(LLView* parent)
-{
- if(parent->getChildCount() == 0 || !containerType(parent)) // if it has no children or isn't a container type, skip it
- {
- return;
- }
-
- // for every child of the parent
- for(child_list_const_iter_t child_it = parent->getChildList()->begin(); child_it != parent->getChildList()->end(); ++child_it)
- {
- LLView* child = *child_it;
- if(overlapIgnorable(child))
- {
- continue;
- }
-
- // for every sibling
- for(child_list_const_iter_t sibling_it = parent->getChildList()->begin(); sibling_it != parent->getChildList()->end(); ++sibling_it) // for each sibling
- {
- LLView* sibling = *sibling_it;
- if(overlapIgnorable(sibling))
- {
- continue;
- }
-
- // 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)
- if(sibling != child && elementOverlap(child, sibling))
- {
- mOverlapPanel->mOverlapMap[child].push_back(sibling); // add to the map
- }
- }
- findOverlapsInChildren(child); // recur
- }
-}
-
-// *HACK: don't overlap with the drag handle and various other elements
-// 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.
-// *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.
-bool LLFloaterUIPreview::overlapIgnorable(LLView* viewp)
-{
- return NULL != dynamic_cast<LLDragHandle*>(viewp) ||
- NULL != dynamic_cast<LLViewBorder*>(viewp) ||
- NULL != dynamic_cast<LLResizeBar*>(viewp);
-}
-
-// *HACK: these are the only two container types as of 8/08, per Richard
-// This is using dynamic casts because there is no object-oriented way to tell which elements are containers.
-bool LLFloaterUIPreview::containerType(LLView* viewp)
-{
- return NULL != dynamic_cast<LLPanel*>(viewp) || NULL != dynamic_cast<LLLayoutStack*>(viewp);
-}
-
-// Check if two llview's rectangles overlap, with some tolerance
-bool LLFloaterUIPreview::elementOverlap(LLView* view1, LLView* view2)
-{
- LLSD rec1 = view1->getRect().getValue();
- LLSD rec2 = view2->getRect().getValue();
- int tolerance = 2;
- return (int)rec1[0] <= (int)rec2[2] - tolerance &&
- (int)rec2[0] <= (int)rec1[2] - tolerance &&
- (int)rec1[3] <= (int)rec2[1] - tolerance &&
- (int)rec2[3] <= (int)rec1[1] - tolerance;
-}
-
-void LLOverlapPanel::draw()
-{
- static const std::string current_selection_text("Current selection: ");
- static const std::string overlapper_text("Overlapper: ");
- LLColor4 text_color = LLColor4::grey;
- gGL.color4fv(text_color.mV);
-
- if(!LLView::sPreviewClickedElement)
- {
- LLUI::translate(5,getRect().getHeight()-20); // translate to top-5,left-5
- LLView::sDrawPreviewHighlights = false;
- LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text, 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
- }
- else
- {
- OverlapMap::iterator iterExists = mOverlapMap.find(LLView::sPreviewClickedElement);
- if(iterExists == mOverlapMap.end())
- {
- return;
- }
-
- std::list<LLView*> overlappers = mOverlapMap[LLView::sPreviewClickedElement];
- if(overlappers.size() == 0)
- {
- LLUI::translate(5,getRect().getHeight()-20); // translate to top-5,left-5
- LLView::sDrawPreviewHighlights = false;
- std::string current_selection = std::string(current_selection_text + LLView::sPreviewClickedElement->getName() + " (no elements overlap)");
- S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(current_selection) + 10;
- LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection, 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
- // widen panel enough to fit this text
- LLRect rect = getRect();
- setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
- return;
- }
-
- // recalculate required with and height; otherwise use cached
- bool need_to_recalculate_bounds = false;
- if(mLastClickedElement == NULL)
- {
- need_to_recalculate_bounds = true;
- }
-
- if(NULL == mLastClickedElement)
- {
- mLastClickedElement = LLView::sPreviewClickedElement;
- }
-
- // recalculate bounds for scroll panel
- if(need_to_recalculate_bounds || LLView::sPreviewClickedElement->getName() != mLastClickedElement->getName())
- {
- // reset panel's rectangle to its default width and height (300x600)
- LLRect panel_rect = getRect();
- setRect(LLRect(panel_rect.mLeft,panel_rect.mTop,panel_rect.mLeft+getRect().getWidth(),panel_rect.mTop-getRect().getHeight()));
-
- LLRect rect;
-
- // change bounds for selected element
- int height_sum = mLastClickedElement->getRect().getHeight() + mSpacing + 80;
- rect = getRect();
- setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() > mLastClickedElement->getRect().getWidth() + 5 ? rect.mRight : rect.mLeft + mLastClickedElement->getRect().getWidth() + 5, rect.mBottom));
-
- // and widen to accomodate text if that's wider
- std::string display_text = current_selection_text + LLView::sPreviewClickedElement->getName();
- S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(display_text) + 10;
- rect = getRect();
- setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
-
- std::list<LLView*> overlappers = mOverlapMap[LLView::sPreviewClickedElement];
- for(std::list<LLView*>::iterator overlap_it = overlappers.begin(); overlap_it != overlappers.end(); ++overlap_it)
- {
- LLView* viewp = *overlap_it;
- height_sum += viewp->getRect().getHeight() + mSpacing*3;
-
- // widen panel's rectangle to accommodate widest overlapping element of this floater
- rect = getRect();
- setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() > viewp->getRect().getWidth() + 5 ? rect.mRight : rect.mLeft + viewp->getRect().getWidth() + 5, rect.mBottom));
-
- // and widen to accomodate text if that's wider
- std::string display_text = overlapper_text + viewp->getName();
- S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(display_text) + 10;
- rect = getRect();
- setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
- }
- // change panel's height to accommodate all element heights plus spacing between them
- rect = getRect();
- setRect(LLRect(rect.mLeft,rect.mTop,rect.mRight,rect.mTop-height_sum));
- }
-
- LLUI::translate(5,getRect().getHeight()-10); // translate to top left
- LLView::sDrawPreviewHighlights = false;
-
- // draw currently-selected element at top of overlappers
- LLUI::translate(0,-mSpacing);
- LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text + LLView::sPreviewClickedElement->getName(), 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
- LLUI::translate(0,-mSpacing-LLView::sPreviewClickedElement->getRect().getHeight()); // skip spacing distance + height
- LLView::sPreviewClickedElement->draw();
-
- for(std::list<LLView*>::iterator overlap_it = overlappers.begin(); overlap_it != overlappers.end(); ++overlap_it)
- {
- LLView* viewp = *overlap_it;
-
- // draw separating line
- LLUI::translate(0,-mSpacing);
- gl_line_2d(0,0,getRect().getWidth()-10,0,LLColor4(192.0f/255.0f,192.0f/255.0f,192.0f/255.0f));
-
- // draw name
- LLUI::translate(0,-mSpacing);
- LLFontGL::getFontSansSerifSmall()->renderUTF8(overlapper_text + viewp->getName(), 0, 0, 0, text_color,
- LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
-
- // draw element
- LLUI::translate(0,-mSpacing-viewp->getRect().getHeight()); // skip spacing distance + height
- viewp->draw();
- }
- mLastClickedElement = LLView::sPreviewClickedElement;
- }
-}
-
-void LLFloaterUIPreviewUtil::registerFloater()
-{
- LLFloaterReg::add("ui_preview", "floater_ui_preview.xml",
- &LLFloaterReg::build<LLFloaterUIPreview>);
-}
+/**
+ * @file llfloateruipreview.cpp
+ * @brief Tool for previewing and editing floaters, plus localization tool integration
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// Tool for previewing floaters and panels for localization and UI design purposes.
+// See: https://wiki.lindenlab.com/wiki/GUI_Preview_And_Localization_Tools
+// See: https://jira.lindenlab.com/browse/DEV-16869
+
+// *TODO: Translate error messgaes using notifications/alerts.xml
+
+#include "llviewerprecompiledheaders.h" // Precompiled headers
+
+#include "llfloateruipreview.h" // Own header
+
+// Internal utility
+#include "lldiriterator.h"
+#include "lleventtimer.h"
+#include "llexternaleditor.h"
+#include "llrender.h"
+#include "llsdutil.h"
+#include "llxmltree.h"
+#include "llviewerwindow.h"
+
+// XUI
+#include "lluictrlfactory.h"
+#include "llcombobox.h"
+#include "llnotificationsutil.h"
+#include "llresizebar.h"
+#include "llscrolllistitem.h"
+#include "llscrolllistctrl.h"
+#include "llfilepicker.h"
+#include "lldraghandle.h"
+#include "lllayoutstack.h"
+#include "lltooltip.h"
+#include "llviewermenu.h"
+#include "llrngwriter.h"
+#include "llfloater.h" // superclass
+#include "llfloaterreg.h"
+#include "llscrollcontainer.h" // scroll container for overlapping elements
+#include "lllivefile.h" // live file poll/stat/reload
+#include "llviewermenufile.h" // LLFilePickerReplyThread
+
+// Boost (for linux/unix command-line execv)
+#include <boost/tokenizer.hpp>
+#include <boost/shared_ptr.hpp>
+
+// External utility
+#include <string>
+#include <list>
+#include <map>
+
+#if LL_DARWIN
+#include <CoreFoundation/CFURL.h>
+#endif
+
+// Static initialization
+static const S32 PRIMARY_FLOATER = 1;
+static const S32 SECONDARY_FLOATER = 2;
+
+class LLOverlapPanel;
+static LLDefaultChildRegistry::Register<LLOverlapPanel> register_overlap_panel("overlap_panel");
+
+static std::string get_xui_dir()
+{
+ std::string delim = gDirUtilp->getDirDelimiter();
+ return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim;
+}
+
+// Forward declarations to avoid header dependencies
+class LLColor;
+class LLScrollListCtrl;
+class LLComboBox;
+class LLButton;
+class LLLineEditor;
+class LLXmlTreeNode;
+class LLFloaterUIPreview;
+class LLFadeEventTimer;
+
+class LLLocalizationResetForcer;
+class LLGUIPreviewLiveFile;
+class LLFadeEventTimer;
+class LLPreviewedFloater;
+
+// Implementation of custom overlapping element display panel
+class LLOverlapPanel : public LLPanel
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLPanel::Params>
+ {
+ Params() {}
+ };
+ LLOverlapPanel(Params p = Params()) : LLPanel(p),
+ mSpacing(10),
+ // mClickedElement(NULL),
+ mLastClickedElement(NULL)
+ {
+ mOriginalWidth = getRect().getWidth();
+ mOriginalHeight = getRect().getHeight();
+ }
+ virtual void draw();
+
+ typedef std::map<LLView*, std::list<LLView*> > OverlapMap;
+ OverlapMap mOverlapMap; // map, of XUI element to a list of XUI elements it overlaps
+
+ // LLView *mClickedElement;
+ LLView *mLastClickedElement;
+ int mOriginalWidth, mOriginalHeight, mSpacing;
+};
+
+
+class LLFloaterUIPreview : public LLFloater
+{
+public:
+ // Setup
+ LLFloaterUIPreview(const LLSD& key);
+ virtual ~LLFloaterUIPreview();
+
+ std::string getLocStr(S32 ID); // fetches the localization string based on what is selected in the drop-down menu
+ void displayFloater(bool click, S32 ID); // needs to be public so live file can call it when it finds an update
+
+ /*virtual*/ bool postBuild();
+ /*virtual*/ void onClose(bool app_quitting);
+
+ void refreshList(); // refresh list (empty it out and fill it up from scratch)
+ void addFloaterEntry(const std::string& path); // add a single file's entry to the list of floaters
+
+ static bool containerType(LLView* viewp); // check if the element is a container type and tree traverses need to look at its children
+
+public:
+ LLPreviewedFloater* mDisplayedFloater; // the floater which is currently being displayed
+ LLPreviewedFloater* mDisplayedFloater_2; // the floater which is currently being displayed
+ LLGUIPreviewLiveFile* mLiveFile; // live file for checking for updates to the currently-displayed XML file
+ LLOverlapPanel* mOverlapPanel; // custom overlapping elements panel
+ // bool mHighlightingDiffs; // bool for whether localization diffs are being highlighted or not
+ bool mHighlightingOverlaps; // bool for whether overlapping elements are being highlighted
+
+ // 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
+ typedef std::list<std::string> StringList;
+ typedef std::shared_ptr<StringList> StringListPtr;
+ typedef std::map<std::string, std::pair<StringListPtr,StringListPtr> > DiffMap;
+ DiffMap mDiffsMap; // map, of filename to pair of list of changed element paths and list of errors
+
+private:
+ LLExternalEditor mExternalEditor;
+
+ // XUI elements for this floater
+ LLScrollListCtrl* mFileList; // scroll list control for file list
+ LLLineEditor* mEditorPathTextBox; // text field for path to editor executable
+ LLLineEditor* mEditorArgsTextBox; // text field for arguments to editor executable
+ LLLineEditor* mDiffPathTextBox; // text field for path to diff file
+ LLButton* mDisplayFloaterBtn; // button to display primary floater
+ LLButton* mDisplayFloaterBtn_2; // button to display secondary floater
+ LLButton* mEditFloaterBtn; // button to edit floater
+ LLButton* mExecutableBrowseButton; // button to browse for executable
+ LLButton* mCloseOtherButton; // button to close primary displayed floater
+ LLButton* mCloseOtherButton_2; // button to close secondary displayed floater
+ LLButton* mDiffBrowseButton; // button to browse for diff file
+ LLButton* mToggleHighlightButton; // button to toggle highlight of files/elements with diffs
+ LLButton* mToggleOverlapButton; // button to togle overlap panel/highlighting
+ LLComboBox* mLanguageSelection; // combo box for primary language selection
+ LLComboBox* mLanguageSelection_2; // combo box for secondary language selection
+ S32 mLastDisplayedX, mLastDisplayedY; // stored position of last floater so the new one opens up in the same place
+ std::string mDelim; // the OS-specific delimiter character (/ or \) (*TODO: this shouldn't be needed, right?)
+
+ std::string mSavedEditorPath; // stored editor path so closing this floater doesn't reset it
+ std::string mSavedEditorArgs; // stored editor args so closing this floater doesn't reset it
+ std::string mSavedDiffPath; // stored diff file path so closing this floater doesn't reset it
+
+ // Internal functionality
+ static void popupAndPrintWarning(const std::string& warning); // pop up a warning
+ std::string getLocalizedDirectory(); // build and return the path to the XUI directory for the currently-selected localization
+ void scanDiffFile(LLXmlTreeNode* file_node); // scan a given XML node for diff entries and highlight them in its associated file
+ void highlightChangedElements(); // look up the list of elements to highlight and highlight them in the current floater
+ void highlightChangedFiles(); // look up the list of changed files to highlight and highlight them in the scroll list
+ void findOverlapsInChildren(LLView* parent); // fill the map below with element overlap information
+ static bool overlapIgnorable(LLView* viewp); // check it the element can be ignored for overlap/localization purposes
+
+ // check if two elements overlap using their rectangles
+ // used instead of llrect functions because by adding a few pixels of leeway I can cut down drastically on the number of overlaps
+ bool elementOverlap(LLView* view1, LLView* view2);
+
+ // Button/drop-down action listeners (self explanatory)
+ void onClickDisplayFloater(S32 id);
+ void onClickSaveFloater(S32 id);
+ void onClickSaveAll(S32 id);
+ void onClickEditFloater();
+ void onClickBrowseForEditor();
+ void getExecutablePath(const std::vector<std::string>& filenames);
+ void onClickBrowseForDiffs();
+ void getDiffsFilePath(const std::vector<std::string>& filenames);
+ void onClickToggleDiffHighlighting();
+ void onClickToggleOverlapping();
+ void onClickCloseDisplayedFloater(S32 id);
+ void onLanguageComboSelect(LLUICtrl* ctrl);
+ void onClickExportSchema();
+ void onClickShowRectangles(const LLSD& data);
+};
+
+//----------------------------------------------------------------------------
+// Local class declarations
+// Reset object to ensure that when we change the current language setting for preview purposes,
+// it automatically is reset. Constructed on the stack at the start of the method; the reset
+// occurs as it falls out of scope at the end of the method. See llfloateruipreview.cpp for usage.
+class LLLocalizationResetForcer
+{
+public:
+ LLLocalizationResetForcer(LLFloaterUIPreview* floater, S32 ID);
+ virtual ~LLLocalizationResetForcer();
+
+private:
+ std::string mSavedLocalization; // the localization before we change it
+};
+
+// Implementation of live file
+// When a floater is being previewed, any saved changes to its corresponding
+// file cause the previewed floater to be reloaded
+class LLGUIPreviewLiveFile : public LLLiveFile
+{
+public:
+ LLGUIPreviewLiveFile(std::string path, std::string name, LLFloaterUIPreview* parent);
+ virtual ~LLGUIPreviewLiveFile();
+ LLFloaterUIPreview* mParent;
+ LLFadeEventTimer* mFadeTimer; // timer for fade-to-yellow-and-back effect to warn that file has been reloaded
+ bool mFirstFade; // setting this avoids showing the fade reload warning on first load
+ std::string mFileName;
+protected:
+ bool loadFile();
+};
+
+// Implementation of graphical fade in/out (on timer) for when XUI files are updated
+class LLFadeEventTimer : public LLEventTimer
+{
+public:
+ LLFadeEventTimer(F32 refresh, LLGUIPreviewLiveFile* parent);
+ bool tick();
+ LLGUIPreviewLiveFile* mParent;
+private:
+ bool mFadingOut; // fades in then out; this is toggled in between
+ LLColor4 mOriginalColor; // original color; color is reset to this after fade is coimplete
+};
+
+// Implementation of previewed floater
+// Used to override draw and mouse handler
+class LLPreviewedFloater : public LLFloater
+{
+public:
+ LLPreviewedFloater(LLFloaterUIPreview* floater, const Params& params)
+ : LLFloater(LLSD(), params),
+ mFloaterUIPreview(floater)
+ {
+ }
+
+ virtual void draw();
+ bool handleRightMouseDown(S32 x, S32 y, MASK mask);
+ bool handleToolTip(S32 x, S32 y, MASK mask);
+ bool selectElement(LLView* parent, int x, int y, int depth); // select element to display its overlappers
+
+ LLFloaterUIPreview* mFloaterUIPreview;
+
+ // draw widget outlines
+ static bool sShowRectangles;
+};
+
+bool LLPreviewedFloater::sShowRectangles = false;
+
+//----------------------------------------------------------------------------
+
+// Localization reset forcer -- ensures that when localization is temporarily changed for previewed floater, it is reset
+// Changes are made here
+LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater, S32 ID)
+{
+ mSavedLocalization = LLUI::getInstance()->mSettingGroups["config"]->getString("Language"); // save current localization setting
+ LLUI::getInstance()->mSettingGroups["config"]->setString("Language", floater->getLocStr(ID));// hack language to be the one we want to preview floaters in
+ // forcibly reset XUI paths with this new language
+ gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), floater->getLocStr(ID));
+}
+
+// Actually reset in destructor
+// Changes are reversed here
+LLLocalizationResetForcer::~LLLocalizationResetForcer()
+{
+ LLUI::getInstance()->mSettingGroups["config"]->setString("Language", mSavedLocalization); // reset language to what it was before we changed it
+ // forcibly reset XUI paths with this new language
+ gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), mSavedLocalization);
+}
+
+// Live file constructor
+// Needs full path for LLLiveFile but needs just file name for this code, hence the reduntant arguments; easier than separating later
+LLGUIPreviewLiveFile::LLGUIPreviewLiveFile(std::string path, std::string name, LLFloaterUIPreview* parent)
+ : mFileName(name),
+ mParent(parent),
+ mFirstFade(true),
+ mFadeTimer(NULL),
+ LLLiveFile(path, 1.0)
+{}
+
+LLGUIPreviewLiveFile::~LLGUIPreviewLiveFile()
+{
+ mParent->mLiveFile = NULL;
+ if(mFadeTimer)
+ {
+ mFadeTimer->mParent = NULL;
+ // deletes itself; see lltimer.cpp
+ }
+}
+
+// Live file load
+bool LLGUIPreviewLiveFile::loadFile()
+{
+ mParent->displayFloater(false,1); // redisplay the floater
+ 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
+ {
+ mFirstFade = false;
+ }
+ else
+ {
+ if(mFadeTimer)
+ {
+ mFadeTimer->mParent = NULL;
+ }
+ mFadeTimer = new LLFadeEventTimer(0.05f,this);
+ }
+ return true;
+}
+
+// Initialize fade event timer
+LLFadeEventTimer::LLFadeEventTimer(F32 refresh, LLGUIPreviewLiveFile* parent)
+ : mParent(parent),
+ mFadingOut(true),
+ LLEventTimer(refresh)
+{
+ mOriginalColor = mParent->mParent->mDisplayedFloater->getBackgroundColor();
+}
+
+// Single tick of fade event timer: increment the color
+bool LLFadeEventTimer::tick()
+{
+ float diff = 0.04f;
+ if(true == mFadingOut) // set fade for in/out color direction
+ {
+ diff = -diff;
+ }
+
+ if(NULL == mParent) // no more need to tick, so suicide
+ {
+ return true;
+ }
+
+ // Set up colors
+ LLColor4 bg_color = mParent->mParent->mDisplayedFloater->getBackgroundColor();
+ LLSD colors = bg_color.getValue();
+ LLSD colors_old = colors;
+
+ // Tick colors
+ colors[0] = colors[0].asReal() - diff; if(colors[0].asReal() < mOriginalColor.getValue()[0].asReal()) { colors[0] = colors_old[0]; }
+ colors[1] = colors[1].asReal() - diff; if(colors[1].asReal() < mOriginalColor.getValue()[1].asReal()) { colors[1] = colors_old[1]; }
+ colors[2] = colors[2].asReal() + diff; if(colors[2].asReal() > mOriginalColor.getValue()[2].asReal()) { colors[2] = colors_old[2]; }
+
+ // Clamp and set colors
+ bg_color.setValue(colors);
+ bg_color.clamp(); // make sure we didn't exceed [0,1]
+ mParent->mParent->mDisplayedFloater->setBackgroundColor(bg_color);
+
+ if(bg_color[2] <= 0.0f) // end of fade out, start fading in
+ {
+ mFadingOut = false;
+ }
+
+ return false;
+}
+
+// Constructor
+LLFloaterUIPreview::LLFloaterUIPreview(const LLSD& key)
+ : LLFloater(key),
+ mDisplayedFloater(NULL),
+ mDisplayedFloater_2(NULL),
+ mLiveFile(NULL),
+ // sHighlightingDiffs(false),
+ mHighlightingOverlaps(false),
+ mLastDisplayedX(0),
+ mLastDisplayedY(0)
+{
+}
+
+// Destructor
+LLFloaterUIPreview::~LLFloaterUIPreview()
+{
+ // spawned floaters are deleted automatically, so we don't need to delete them here
+
+ // save contents of textfields so it can be restored later if the floater is created again this session
+ mSavedEditorPath = mEditorPathTextBox->getText();
+ mSavedEditorArgs = mEditorArgsTextBox->getText();
+ mSavedDiffPath = mDiffPathTextBox->getText();
+
+ // delete live file if it exists
+ if(mLiveFile)
+ {
+ delete mLiveFile;
+ mLiveFile = NULL;
+ }
+}
+
+// Perform post-build setup (defined in superclass)
+bool LLFloaterUIPreview::postBuild()
+{
+ LLPanel* main_panel_tmp = getChild<LLPanel>("main_panel"); // get a pointer to the main panel in order to...
+ mFileList = main_panel_tmp->getChild<LLScrollListCtrl>("name_list"); // save pointer to file list
+ // Double-click opens the floater, for convenience
+ mFileList->setDoubleClickCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, PRIMARY_FLOATER));
+
+ setDefaultBtn("display_floater");
+ // get pointers to buttons and link to callbacks
+ mLanguageSelection = main_panel_tmp->getChild<LLComboBox>("language_select_combo");
+ mLanguageSelection->setCommitCallback(boost::bind(&LLFloaterUIPreview::onLanguageComboSelect, this, mLanguageSelection));
+ mLanguageSelection_2 = main_panel_tmp->getChild<LLComboBox>("language_select_combo_2");
+ mLanguageSelection_2->setCommitCallback(boost::bind(&LLFloaterUIPreview::onLanguageComboSelect, this, mLanguageSelection));
+ LLPanel* editor_panel_tmp = main_panel_tmp->getChild<LLPanel>("editor_panel");
+ mDisplayFloaterBtn = main_panel_tmp->getChild<LLButton>("display_floater");
+ mDisplayFloaterBtn->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, PRIMARY_FLOATER));
+ mDisplayFloaterBtn_2 = main_panel_tmp->getChild<LLButton>("display_floater_2");
+ mDisplayFloaterBtn_2->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickDisplayFloater, this, SECONDARY_FLOATER));
+ mToggleOverlapButton = main_panel_tmp->getChild<LLButton>("toggle_overlap_panel");
+ mToggleOverlapButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickToggleOverlapping, this));
+ mCloseOtherButton = main_panel_tmp->getChild<LLButton>("close_displayed_floater");
+ mCloseOtherButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickCloseDisplayedFloater, this, PRIMARY_FLOATER));
+ mCloseOtherButton_2 = main_panel_tmp->getChild<LLButton>("close_displayed_floater_2");
+ mCloseOtherButton_2->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickCloseDisplayedFloater, this, SECONDARY_FLOATER));
+ mEditFloaterBtn = main_panel_tmp->getChild<LLButton>("edit_floater");
+ mEditFloaterBtn->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickEditFloater, this));
+ mExecutableBrowseButton = editor_panel_tmp->getChild<LLButton>("browse_for_executable");
+ LLPanel* vlt_panel_tmp = main_panel_tmp->getChild<LLPanel>("vlt_panel");
+ mExecutableBrowseButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickBrowseForEditor, this));
+ mDiffBrowseButton = vlt_panel_tmp->getChild<LLButton>("browse_for_vlt_diffs");
+ mDiffBrowseButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickBrowseForDiffs, this));
+ mToggleHighlightButton = vlt_panel_tmp->getChild<LLButton>("toggle_vlt_diff_highlight");
+ mToggleHighlightButton->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickToggleDiffHighlighting, this));
+ main_panel_tmp->getChild<LLButton>("save_floater")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickSaveFloater, this, PRIMARY_FLOATER));
+ main_panel_tmp->getChild<LLButton>("save_all_floaters")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickSaveAll, this, PRIMARY_FLOATER));
+
+ getChild<LLButton>("export_schema")->setClickedCallback(boost::bind(&LLFloaterUIPreview::onClickExportSchema, this));
+ getChild<LLUICtrl>("show_rectangles")->setCommitCallback(
+ boost::bind(&LLFloaterUIPreview::onClickShowRectangles, this, _2));
+
+ // get pointers to text fields
+ mEditorPathTextBox = editor_panel_tmp->getChild<LLLineEditor>("executable_path_field");
+ mEditorArgsTextBox = editor_panel_tmp->getChild<LLLineEditor>("executable_args_field");
+ mDiffPathTextBox = vlt_panel_tmp->getChild<LLLineEditor>("vlt_diff_path_field");
+
+ // *HACK: restored saved editor path and args to textfields
+ mEditorPathTextBox->setText(mSavedEditorPath);
+ mEditorArgsTextBox->setText(mSavedEditorArgs);
+ mDiffPathTextBox->setText(mSavedDiffPath);
+
+ // Set up overlap panel
+ mOverlapPanel = getChild<LLOverlapPanel>("overlap_panel");
+
+ getChildView("overlap_scroll")->setVisible( mHighlightingOverlaps);
+
+ mDelim = gDirUtilp->getDirDelimiter(); // initialize delimiter to dir sep slash
+
+ // refresh list of available languages (EN will still be default)
+ bool found = true;
+ bool found_en_us = false;
+ std::string language_directory;
+ std::string xui_dir = get_xui_dir(); // directory containing localizations -- don't forget trailing delim
+ mLanguageSelection->removeall(); // clear out anything temporarily in list from XML
+
+ LLDirIterator iter(xui_dir, "*");
+ while(found) // for every directory
+ {
+ if((found = iter.next(language_directory))) // get next directory
+ {
+ std::string full_path = gDirUtilp->add(xui_dir, language_directory);
+ if(LLFile::isfile(full_path.c_str())) // if it's not a directory, skip it
+ {
+ continue;
+ }
+
+ if(strncmp("template",language_directory.c_str(),8) && -1 == language_directory.find(".")) // if it's not the template directory or a hidden directory
+ {
+ if(!strncmp("en",language_directory.c_str(),5)) // remember if we've seen en, so we can make it default
+ {
+ found_en_us = true;
+ }
+ else
+ {
+ mLanguageSelection->add(std::string(language_directory)); // add it to the language selection dropdown menu
+ mLanguageSelection_2->add(std::string(language_directory));
+ }
+ }
+ }
+ }
+ if(found_en_us)
+ {
+ mLanguageSelection->add(std::string("en"),ADD_TOP); // make en first item if we found it
+ mLanguageSelection_2->add(std::string("en"),ADD_TOP);
+ }
+ else
+ {
+ std::string warning = std::string("No EN localization found; check your XUI directories!");
+ popupAndPrintWarning(warning);
+ }
+ mLanguageSelection->selectFirstItem(); // select the first item
+ mLanguageSelection_2->selectFirstItem();
+
+ refreshList(); // refresh the list of available floaters
+
+ return true;
+}
+
+// Callback for language combo box selection: refresh current floater when you change languages
+void LLFloaterUIPreview::onLanguageComboSelect(LLUICtrl* ctrl)
+{
+ LLComboBox* caller = dynamic_cast<LLComboBox*>(ctrl);
+ if (!caller)
+ return;
+ if(caller->getName() == std::string("language_select_combo"))
+ {
+ if(mDisplayedFloater)
+ {
+ onClickCloseDisplayedFloater(PRIMARY_FLOATER);
+ displayFloater(true,1);
+ }
+ }
+ else
+ {
+ if(mDisplayedFloater_2)
+ {
+ onClickCloseDisplayedFloater(PRIMARY_FLOATER);
+ displayFloater(true,2); // *TODO: make take an arg
+ }
+ }
+
+}
+
+void LLFloaterUIPreview::onClickExportSchema()
+{
+ //NOTE: schema generation not complete
+ //gViewerWindow->setCursor(UI_CURSOR_WAIT);
+ //std::string template_path = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "xui", "schema");
+
+ //typedef LLWidgetTypeRegistry::Registrar::registry_map_t::const_iterator registry_it;
+ //registry_it end_it = LLWidgetTypeRegistry::defaultRegistrar().endItems();
+ //for(registry_it it = LLWidgetTypeRegistry::defaultRegistrar().beginItems();
+ // it != end_it;
+ // ++it)
+ //{
+ // std::string widget_name = it->first;
+ // const LLInitParam::BaseBlock& block =
+ // (*LLDefaultParamBlockRegistry::instance().getValue(*LLWidgetTypeRegistry::instance().getValue(widget_name)))();
+ // LLXMLNodePtr root_nodep = new LLXMLNode();
+ // LLRNGWriter().writeRNG(widget_name, root_nodep, block, "http://www.lindenlab.com/xui");
+
+ // std::string file_name(template_path + gDirUtilp->getDirDelimiter() + widget_name + ".rng");
+
+ // LLFILE* rng_file = LLFile::fopen(file_name.c_str(), "w");
+ // {
+ // LLXMLNode::writeHeaderToFile(rng_file);
+ // const bool use_type_decorations = false;
+ // root_nodep->writeToFile(rng_file, std::string(), use_type_decorations);
+ // }
+ // fclose(rng_file);
+ //}
+ //gViewerWindow->setCursor(UI_CURSOR_ARROW);
+}
+
+void LLFloaterUIPreview::onClickShowRectangles(const LLSD& data)
+{
+ LLPreviewedFloater::sShowRectangles = data.asBoolean();
+}
+
+// Close click handler -- delete my displayed floater if it exists
+void LLFloaterUIPreview::onClose(bool app_quitting)
+{
+ if(!app_quitting && mDisplayedFloater)
+ {
+ onClickCloseDisplayedFloater(PRIMARY_FLOATER);
+ onClickCloseDisplayedFloater(SECONDARY_FLOATER);
+ delete mDisplayedFloater;
+ mDisplayedFloater = NULL;
+ delete mDisplayedFloater_2;
+ mDisplayedFloater_2 = NULL;
+ }
+}
+
+// Error handling (to avoid code repetition)
+// *TODO: this is currently unlocalized. Add to alerts/notifications.xml, someday, maybe.
+void LLFloaterUIPreview::popupAndPrintWarning(const std::string& warning)
+{
+ LL_WARNS() << warning << LL_ENDL;
+ LLSD args;
+ args["MESSAGE"] = warning;
+ LLNotificationsUtil::add("GenericAlert", args);
+}
+
+// Get localization string from drop-down menu
+std::string LLFloaterUIPreview::getLocStr(S32 ID)
+{
+ if(ID == 1)
+ {
+ return mLanguageSelection->getSelectedItemLabel(0);
+ }
+ else
+ {
+ return mLanguageSelection_2->getSelectedItemLabel(0);
+ }
+}
+
+// Get localized directory (build path from data directory to XUI files, substituting localization string in for language)
+std::string LLFloaterUIPreview::getLocalizedDirectory()
+{
+ return get_xui_dir() + (getLocStr(1)) + mDelim; // e.g. "C:/Code/guipreview/indra/newview/skins/xui/en/";
+}
+
+// Refresh the list of floaters by doing a directory traverse for XML XUI floater files
+// Could be used to grab any specific language's list of compatible floaters, but currently it's just used to get all of them
+void LLFloaterUIPreview::refreshList()
+{
+ // Note: the mask doesn't seem to accept regular expressions, so there need to be two directory searches here
+ mFileList->clearRows(); // empty list
+ std::string name;
+ bool found = true;
+
+ LLDirIterator floater_iter(getLocalizedDirectory(), "floater_*.xml");
+ while(found) // for every floater file that matches the pattern
+ {
+ if((found = floater_iter.next(name))) // get next file matching pattern
+ {
+ addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
+ }
+ }
+ found = true;
+
+ LLDirIterator inspect_iter(getLocalizedDirectory(), "inspect_*.xml");
+ while(found) // for every inspector file that matches the pattern
+ {
+ if((found = inspect_iter.next(name))) // get next file matching pattern
+ {
+ addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
+ }
+ }
+ found = true;
+
+ LLDirIterator menu_iter(getLocalizedDirectory(), "menu_*.xml");
+ while(found) // for every menu file that matches the pattern
+ {
+ if((found = menu_iter.next(name))) // get next file matching pattern
+ {
+ addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
+ }
+ }
+ found = true;
+
+ LLDirIterator panel_iter(getLocalizedDirectory(), "panel_*.xml");
+ while(found) // for every panel file that matches the pattern
+ {
+ if((found = panel_iter.next(name))) // get next file matching pattern
+ {
+ addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
+ }
+ }
+ found = true;
+
+ LLDirIterator sidepanel_iter(getLocalizedDirectory(), "sidepanel_*.xml");
+ while(found) // for every sidepanel file that matches the pattern
+ {
+ if((found = sidepanel_iter.next(name))) // get next file matching pattern
+ {
+ addFloaterEntry(name.c_str()); // and add it to the list (file name only; localization code takes care of rest of path)
+ }
+ }
+
+ 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)
+ {
+ mFileList->selectFirstItem();
+ }
+}
+
+// Add a single entry to the list of available floaters
+// Note: no deduplification (shouldn't be necessary)
+void LLFloaterUIPreview::addFloaterEntry(const std::string& path)
+{
+ LLUUID* entry_id = new LLUUID(); // create a new UUID
+ entry_id->generate(path);
+ const LLUUID& entry_id_ref = *entry_id; // get a reference to the UUID for the LLSD block
+
+ // fill LLSD column entry: initialize row/col structure
+ LLSD row;
+ row["id"] = entry_id_ref;
+ LLSD& columns = row["columns"];
+
+ // Get name of floater:
+ LLXmlTree xml_tree;
+ std::string full_path = getLocalizedDirectory() + path; // get full path
+ bool success = xml_tree.parseFile(full_path.c_str(), true); // parse xml
+ std::string entry_name;
+ std::string entry_title;
+ if(success)
+ {
+ // get root (or error handle)
+ LLXmlTreeNode* root_floater = xml_tree.getRoot();
+ if (!root_floater)
+ {
+ std::string warning = std::string("No root node found in XUI file: ") + path;
+ popupAndPrintWarning(warning);
+ return;
+ }
+
+ // get name
+ root_floater->getAttributeString("name",entry_name);
+ if(std::string("") == entry_name)
+ {
+ entry_name = "Error: unable to load " + std::string(path); // set to error state if load fails
+ }
+
+ // get title
+ 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
+ }
+ else
+ {
+ std::string warning = std::string("Unable to parse XUI file: ") + path; // error handling
+ popupAndPrintWarning(warning);
+ if(mLiveFile)
+ {
+ delete mLiveFile;
+ mLiveFile = NULL;
+ }
+ return;
+ }
+
+ // Fill floater title column
+ columns[0]["column"] = "title_column";
+ columns[0]["type"] = "text";
+ columns[0]["value"] = entry_title;
+
+ // Fill floater path column
+ columns[1]["column"] = "file_column";
+ columns[1]["type"] = "text";
+ columns[1]["value"] = std::string(path);
+
+ // Fill floater name column
+ columns[2]["column"] = "top_level_node_column";
+ columns[2]["type"] = "text";
+ columns[2]["value"] = entry_name;
+
+ mFileList->addElement(row); // actually add to list
+}
+
+// Respond to button click to display/refresh currently-selected floater
+void LLFloaterUIPreview::onClickDisplayFloater(S32 caller_id)
+{
+ displayFloater(true, caller_id);
+}
+
+// Saves the current floater/panel
+void LLFloaterUIPreview::onClickSaveFloater(S32 caller_id)
+{
+ displayFloater(true, caller_id);
+ popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files");
+}
+
+// Saves all floater/panels
+void LLFloaterUIPreview::onClickSaveAll(S32 caller_id)
+{
+ int listSize = mFileList->getItemCount();
+
+ for (int index = 0; index < listSize; index++)
+ {
+ mFileList->selectNthItem(index);
+ displayFloater(true, caller_id);
+ }
+ popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files");
+}
+
+// Actually display the floater
+// 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;
+// otherwise, we get an infinite loop as the live file keeps recreating itself. That means this function is generally called twice.
+void LLFloaterUIPreview::displayFloater(bool click, S32 ID)
+{
+ // Convince UI that we're in a different language (the one selected on the drop-down menu)
+ LLLocalizationResetForcer reset_forcer(this, ID); // save old language in reset forcer object (to be reset upon destruction when it falls out of scope)
+
+ LLPreviewedFloater** floaterp = (ID == 1 ? &(mDisplayedFloater) : &(mDisplayedFloater_2));
+ if(ID == 1)
+ {
+ bool floater_already_open = mDisplayedFloater != NULL;
+ if(floater_already_open) // if we are already displaying a floater
+ {
+ mLastDisplayedX = mDisplayedFloater->calcScreenRect().mLeft; // save floater's last known position to put the new one there
+ mLastDisplayedY = mDisplayedFloater->calcScreenRect().mBottom;
+ delete mDisplayedFloater; // delete it (this closes it too)
+ mDisplayedFloater = NULL; // and reset the pointer
+ }
+ }
+ else
+ {
+ if(mDisplayedFloater_2 != NULL)
+ {
+ delete mDisplayedFloater_2;
+ mDisplayedFloater_2 = NULL;
+ }
+ }
+
+ std::string path = mFileList->getSelectedItemLabel(1); // get the path of the currently-selected floater
+ if(std::string("") == path) // if no item is selected
+ {
+ return; // ignore click (this can only happen with empty list; otherwise an item is always selected)
+ }
+
+ LLFloater::Params p(LLFloater::getDefaultParams());
+ p.min_height=p.header_height;
+ p.min_width=10;
+
+ *floaterp = new LLPreviewedFloater(this, p);
+
+ if(!strncmp(path.c_str(),"floater_",8)
+ || !strncmp(path.c_str(), "inspect_", 8)) // if it's a floater
+ {
+ (*floaterp)->buildFromFile(path); // just build it
+ (*floaterp)->openFloater((*floaterp)->getKey());
+ (*floaterp)->setCanResize((*floaterp)->isResizable());
+ }
+ else if (!strncmp(path.c_str(),"menu_",5)) // if it's a menu
+ {
+ // former 'save' processing excised
+ }
+ else // if it is a panel...
+ {
+ (*floaterp)->setCanResize(true);
+
+ const LLFloater::Params& floater_params = LLFloater::getDefaultParams();
+ S32 floater_header_size = floater_params.header_height;
+
+ LLPanel::Params panel_params;
+ LLPanel* panel = LLUICtrlFactory::create<LLPanel>(panel_params); // create a new panel
+
+ panel->buildFromFile(path); // build it
+ panel->setOrigin(2,2); // reset its origin point so it's not offset by -left or other XUI attributes
+ (*floaterp)->setTitle(path); // use the file name as its title, since panels have no guaranteed meaningful name attribute
+ panel->setUseBoundingRect(true); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements)
+ panel->updateBoundingRect(); // update bounding rect
+ LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect
+ LLRect new_rect = panel->getRect(); // get the panel's rect
+ new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible
+ LLRect floater_rect = new_rect;
+ floater_rect.stretch(4, 4);
+ (*floaterp)->reshape(floater_rect.getWidth(), floater_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions
+ panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed)
+ (*floaterp)->addChild(panel); // add panel as child
+ (*floaterp)->openFloater(); // open floater (needed?)
+ }
+
+ if(ID == 1)
+ {
+ (*floaterp)->setOrigin(mLastDisplayedX, mLastDisplayedY);
+ }
+
+ // *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,
+ // resulting in a double free
+ (*floaterp)->setCanClose(false);
+
+ if(ID == 1)
+ {
+ mCloseOtherButton->setEnabled(true); // enable my floater's close button
+ }
+ else
+ {
+ mCloseOtherButton_2->setEnabled(true);
+ }
+
+ // Add localization to title so user knows whether it's localized or defaulted to en
+ std::string full_path = getLocalizedDirectory() + path;
+ std::string floater_lang = "EN";
+ llstat dummy;
+ if(!LLFile::stat(full_path.c_str(), &dummy)) // if the file does not exist
+ {
+ floater_lang = getLocStr(ID);
+ }
+ std::string new_title = (*floaterp)->getTitle() + std::string(" [") + floater_lang +
+ (ID == 1 ? " - Primary" : " - Secondary") + std::string("]");
+ (*floaterp)->setTitle(new_title);
+
+ (*floaterp)->center();
+ addDependentFloater(*floaterp);
+
+ if(click && ID == 1)
+ {
+ // set up live file to track it
+ if(mLiveFile)
+ {
+ delete mLiveFile;
+ mLiveFile = NULL;
+ }
+ mLiveFile = new LLGUIPreviewLiveFile(std::string(full_path.c_str()),std::string(path.c_str()),this);
+ mLiveFile->checkAndReload();
+ mLiveFile->addToEventTimer();
+ }
+
+ if(ID == 1)
+ {
+ mToggleOverlapButton->setEnabled(true);
+ }
+
+ if(LLView::sHighlightingDiffs && click && ID == 1)
+ {
+ highlightChangedElements();
+ }
+
+ if(ID == 1)
+ {
+ mOverlapPanel->mOverlapMap.clear();
+ LLView::sPreviewClickedElement = NULL; // stop overlapping elements from drawing
+ mOverlapPanel->mLastClickedElement = NULL;
+ findOverlapsInChildren((LLView*)mDisplayedFloater);
+
+ // highlight and enable them
+ if(mHighlightingOverlaps)
+ {
+ for(LLOverlapPanel::OverlapMap::iterator iter = mOverlapPanel->mOverlapMap.begin(); iter != mOverlapPanel->mOverlapMap.end(); ++iter)
+ {
+ LLView* viewp = iter->first;
+ LLView::sPreviewHighlightedElements.insert(viewp);
+ }
+ }
+ else if(LLView::sHighlightingDiffs)
+ {
+ highlightChangedElements();
+ }
+ }
+
+ // NOTE: language is reset here automatically when the reset forcer object falls out of scope (see header for details)
+}
+
+// Respond to button click to edit currently-selected floater
+void LLFloaterUIPreview::onClickEditFloater()
+{
+ // Determine file to edit.
+ std::string file_path;
+ {
+ std::string file_name = mFileList->getSelectedItemLabel(1); // get the file name of the currently-selected floater
+ if (file_name.empty()) // if no item is selected
+ {
+ LL_WARNS() << "No file selected" << LL_ENDL;
+ return; // ignore click
+ }
+ file_path = getLocalizedDirectory() + file_name;
+
+ // 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)
+ llstat dummy;
+ if(LLFile::stat(file_path.c_str(), &dummy)) // if the file does not exist
+ {
+ popupAndPrintWarning("No file for this floater exists in the selected localization. Opening the EN version instead.");
+ file_path = get_xui_dir() + mDelim + "en" + mDelim + file_name; // open the en version instead, by default
+ }
+ }
+
+ // Set the editor command.
+ std::string cmd_override;
+ {
+ std::string bin = mEditorPathTextBox->getText();
+ if (!bin.empty())
+ {
+ // surround command with double quotes for the case if the path contains spaces
+ if (bin.find("\"") == std::string::npos)
+ {
+ bin = "\"" + bin + "\"";
+ }
+
+ std::string args = mEditorArgsTextBox->getText();
+ cmd_override = bin + " " + args;
+ }
+ }
+
+ LLExternalEditor::EErrorCode status = mExternalEditor.setCommand("LL_XUI_EDITOR", cmd_override);
+ if (status != LLExternalEditor::EC_SUCCESS)
+ {
+ std::string warning;
+
+ if (status == LLExternalEditor::EC_NOT_SPECIFIED) // Use custom message for this error.
+ {
+ warning = getString("ExternalEditorNotSet");
+ }
+ else
+ {
+ warning = LLExternalEditor::getErrorMessage(status);
+ }
+
+ popupAndPrintWarning(warning);
+ return;
+ }
+
+ // Run the editor.
+ if (mExternalEditor.run(file_path) != LLExternalEditor::EC_SUCCESS)
+ {
+ popupAndPrintWarning(LLExternalEditor::getErrorMessage(status));
+ return;
+ }
+}
+
+// Respond to button click to browse for an executable with which to edit XML files
+void LLFloaterUIPreview::onClickBrowseForEditor()
+{
+ // Let the user choose an executable through the file picker dialog box
+ LLFilePickerReplyThread::startPicker(boost::bind(&LLFloaterUIPreview::getExecutablePath, this, _1), LLFilePicker::FFLOAD_EXE, false);
+}
+
+void LLFloaterUIPreview::getExecutablePath(const std::vector<std::string>& filenames)
+{
+ // put the selected path into text field
+ const std::string chosen_path = filenames[0];
+ std::string executable_path = chosen_path;
+#if LL_DARWIN
+ // on Mac, if it's an application bundle, figure out the actual path from the Info.plist file
+ CFStringRef path_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, chosen_path.c_str(), kCFStringEncodingMacRoman); // get path as a CFStringRef
+ CFURLRef path_url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path_cfstr, kCFURLPOSIXPathStyle, true); // turn it into a CFURLRef
+ CFBundleRef chosen_bundle = CFBundleCreate(kCFAllocatorDefault, path_url); // get a handle for the bundle
+ if(NULL != chosen_bundle)
+ {
+ CFDictionaryRef bundleInfoDict = CFBundleGetInfoDictionary(chosen_bundle); // get the bundle's dictionary
+ if(NULL != bundleInfoDict)
+ {
+ CFStringRef executable_cfstr = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, CFSTR("CFBundleExecutable")); // get the name of the actual executable (e.g. TextEdit or firefox-bin)
+ int max_file_length = 256; // (max file name length is 255 in OSX)
+ char executable_buf[max_file_length];
+ if(CFStringGetCString(executable_cfstr, executable_buf, max_file_length, kCFStringEncodingMacRoman)) // convert CFStringRef to char*
+ {
+ executable_path += std::string("/Contents/MacOS/") + std::string(executable_buf); // append path to executable directory and then executable name to exec path
+ }
+ else
+ {
+ std::string warning = "Unable to get CString from CFString for executable path";
+ popupAndPrintWarning(warning);
+ }
+ }
+ else
+ {
+ std::string warning = "Unable to get bundle info dictionary from application bundle";
+ popupAndPrintWarning(warning);
+ }
+ }
+ else
+ {
+ 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
+ {
+ std::string warning = std::string("Unable to get bundle from path \"") + chosen_path + std::string("\"");
+ popupAndPrintWarning(warning);
+ }
+ }
+
+#endif
+ mEditorPathTextBox->setText(std::string(executable_path)); // copy the path to the executable to the textfield for display and later fetching
+}
+
+// Respond to button click to browse for a VLT-generated diffs file
+void LLFloaterUIPreview::onClickBrowseForDiffs()
+{
+ // create load dialog box
+ LLFilePickerReplyThread::startPicker(boost::bind(&LLFloaterUIPreview::getDiffsFilePath, this, _1), LLFilePicker::FFLOAD_XML, false);
+}
+
+void LLFloaterUIPreview::getDiffsFilePath(const std::vector<std::string>& filenames)
+{
+ // put the selected path into text field
+ const std::string chosen_path = filenames[0];
+ mDiffPathTextBox->setText(std::string(chosen_path)); // copy the path to the executable to the textfield for display and later fetching
+ if(LLView::sHighlightingDiffs) // if we're already highlighting, toggle off and then on so we get the data from the new file
+ {
+ onClickToggleDiffHighlighting();
+ onClickToggleDiffHighlighting();
+ }
+}
+
+void LLFloaterUIPreview::onClickToggleDiffHighlighting()
+{
+ if(mHighlightingOverlaps)
+ {
+ onClickToggleOverlapping();
+ mToggleOverlapButton->toggleState();
+ }
+
+ LLView::sPreviewHighlightedElements.clear(); // clear lists first
+ mDiffsMap.clear();
+ mFileList->clearHighlightedItems();
+
+ if(LLView::sHighlightingDiffs) // Turning highlighting off
+ {
+ LLView::sHighlightingDiffs = !sHighlightingDiffs;
+ return;
+ }
+ else // Turning highlighting on
+ {
+ // Get the file and make sure it exists
+ std::string path_in_textfield = mDiffPathTextBox->getText(); // get file path
+ bool error = false;
+
+ if(std::string("") == path_in_textfield) // check for blank file
+ {
+ std::string warning = "Unable to highlight differences because no file was provided; fill in the relevant text field";
+ popupAndPrintWarning(warning);
+ error = true;
+ }
+
+ llstat dummy;
+ 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)
+ {
+ std::string warning = std::string("Unable to highlight differences because an invalid path to a difference file was provided:\"") + path_in_textfield + "\"";
+ popupAndPrintWarning(warning);
+ error = true;
+ }
+
+ // Build a list of changed elements as given by the XML
+ std::list<std::string> changed_element_names;
+ LLXmlTree xml_tree;
+ bool success = xml_tree.parseFile(path_in_textfield.c_str(), true);
+
+ if(success && !error)
+ {
+ LLXmlTreeNode* root_floater = xml_tree.getRoot();
+ if(!strncmp("XuiDelta",root_floater->getName().c_str(),9))
+ {
+ 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?)
+ child != NULL;
+ child = root_floater->getNextChild()) // get child for next iteration
+ {
+ if(!strncmp("file",child->getName().c_str(),5))
+ {
+ scanDiffFile(child);
+ }
+ else if(!strncmp("error",child->getName().c_str(),6))
+ {
+ std::string error_file, error_message;
+ child->getAttributeString("filename",error_file);
+ child->getAttributeString("message",error_message);
+ if(mDiffsMap.find(error_file) != mDiffsMap.end())
+ {
+ mDiffsMap.insert(std::make_pair(error_file,std::make_pair(StringListPtr(new StringList), StringListPtr(new StringList))));
+ }
+ mDiffsMap[error_file].second->push_back(error_message);
+ }
+ else
+ {
+ std::string warning = std::string("Child was neither a file or an error, but rather the following:\"") + std::string(child->getName()) + "\"";
+ popupAndPrintWarning(warning);
+ error = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ std::string warning = std::string("Root node not named XuiDelta:\"") + path_in_textfield + "\"";
+ popupAndPrintWarning(warning);
+ error = true;
+ }
+ }
+ else if(!error)
+ {
+ std::string warning = std::string("Unable to create tree from XML:\"") + path_in_textfield + "\"";
+ popupAndPrintWarning(warning);
+ error = true;
+ }
+
+ if(error) // if we encountered an error, reset the button to off
+ {
+ mToggleHighlightButton->setToggleState(false);
+ }
+ else // only toggle if we didn't encounter an error
+ {
+ LLView::sHighlightingDiffs = !sHighlightingDiffs;
+ highlightChangedElements(); // *TODO: this is extraneous, right?
+ highlightChangedFiles(); // *TODO: this is extraneous, right?
+ }
+ }
+}
+
+void LLFloaterUIPreview::scanDiffFile(LLXmlTreeNode* file_node)
+{
+ // Get file name
+ std::string file_name;
+ file_node->getAttributeString("name",file_name);
+ if(std::string("") == file_name)
+ {
+ std::string warning = std::string("Empty file name encountered in differences:\"") + file_name + "\"";
+ popupAndPrintWarning(warning);
+ return;
+ }
+
+ // Get a list of changed elements
+ // Get the first child first, then below get the next one; otherwise the iterator is invalid (bug or feature in XML code?)
+ for (LLXmlTreeNode* child = file_node->getFirstChild(); child != NULL; child = file_node->getNextChild())
+ {
+ if(!strncmp("delta",child->getName().c_str(),6))
+ {
+ std::string id;
+ child->getAttributeString("id",id);
+ if(mDiffsMap.find(file_name) == mDiffsMap.end())
+ {
+ mDiffsMap.insert(std::make_pair(file_name,std::make_pair(StringListPtr(new StringList), StringListPtr(new StringList))));
+ }
+ mDiffsMap[file_name].first->push_back(std::string(id.c_str()));
+ }
+ else
+ {
+ std::string warning = std::string("Child of file was not a delta, but rather the following:\"") + std::string(child->getName()) + "\"";
+ popupAndPrintWarning(warning);
+ return;
+ }
+ }
+}
+
+void LLFloaterUIPreview::highlightChangedElements()
+{
+ if(NULL == mLiveFile)
+ {
+ return;
+ }
+
+ // Process differences first (we want their warnings to be shown underneath other warnings)
+ StringListPtr changed_element_paths;
+ DiffMap::iterator iterExists = mDiffsMap.find(mLiveFile->mFileName);
+ if(iterExists != mDiffsMap.end())
+ {
+ changed_element_paths = mDiffsMap[mLiveFile->mFileName].first; // retrieve list of changed element paths from map
+ }
+
+ for(std::list<std::string>::iterator iter = changed_element_paths->begin(); iter != changed_element_paths->end(); ++iter) // for every changed element path
+ {
+ LLView* element = mDisplayedFloater;
+ if(!strncmp(iter->c_str(),".",1)) // if it's the root floater itself
+ {
+ continue;
+ }
+
+ // 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.)
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+ tokenizer tokens(*iter, sep);
+ tokenizer::iterator token_iter;
+ bool failed = false;
+ for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter)
+ {
+ element = element->findChild<LLView>(*token_iter,false); // try to find element: don't recur, and don't create if missing
+
+ // if we still didn't find it...
+ if(NULL == element)
+ {
+ LL_INFOS() << "Unable to find element in XuiDelta file named \"" << *iter << "\" in file \"" << mLiveFile->mFileName <<
+ "\". 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." << LL_ENDL;
+ failed = true;
+ break;
+ }
+ }
+
+ if(!failed)
+ {
+ // Now that we have a pointer to the actual element, add it to the list of elements to be highlighted
+ std::set<LLView*>::iterator iter2 = std::find(LLView::sPreviewHighlightedElements.begin(), LLView::sPreviewHighlightedElements.end(), element);
+ if(iter2 == LLView::sPreviewHighlightedElements.end())
+ {
+ LLView::sPreviewHighlightedElements.insert(element);
+ }
+ }
+ }
+
+ // Process errors second, so their warnings show up on top of other warnings
+ StringListPtr error_list;
+ if(iterExists != mDiffsMap.end())
+ {
+ error_list = mDiffsMap[mLiveFile->mFileName].second;
+ }
+ for(std::list<std::string>::iterator iter = error_list->begin(); iter != error_list->end(); ++iter) // for every changed element path
+ {
+ std::string warning = std::string("Error listed among differences. Filename: \"") + mLiveFile->mFileName + "\". Message: \"" + *iter + "\"";
+ popupAndPrintWarning(warning);
+ }
+}
+
+void LLFloaterUIPreview::highlightChangedFiles()
+{
+ for(DiffMap::iterator iter = mDiffsMap.begin(); iter != mDiffsMap.end(); ++iter) // for every file listed in diffs
+ {
+ LLScrollListItem* item = mFileList->getItemByLabel(std::string(iter->first), false, 1);
+ if(item)
+ {
+ item->setHighlighted(true);
+ }
+ }
+}
+
+// Respond to button click to browse for an executable with which to edit XML files
+void LLFloaterUIPreview::onClickCloseDisplayedFloater(S32 caller_id)
+{
+ if(caller_id == PRIMARY_FLOATER)
+ {
+ mCloseOtherButton->setEnabled(false);
+ mToggleOverlapButton->setEnabled(false);
+
+ if(mDisplayedFloater)
+ {
+ mLastDisplayedX = mDisplayedFloater->calcScreenRect().mLeft;
+ mLastDisplayedY = mDisplayedFloater->calcScreenRect().mBottom;
+ delete mDisplayedFloater;
+ mDisplayedFloater = NULL;
+ }
+
+ if(mLiveFile)
+ {
+ delete mLiveFile;
+ mLiveFile = NULL;
+ }
+
+ if(mToggleOverlapButton->getToggleState())
+ {
+ mToggleOverlapButton->toggleState();
+ onClickToggleOverlapping();
+ }
+
+ LLView::sPreviewClickedElement = NULL; // stop overlapping elements panel from drawing
+ mOverlapPanel->mLastClickedElement = NULL;
+ }
+ else
+ {
+ mCloseOtherButton_2->setEnabled(false);
+ delete mDisplayedFloater_2;
+ mDisplayedFloater_2 = NULL;
+ }
+
+}
+
+void append_view_tooltip(LLView* tooltip_view, std::string *tooltip_msg)
+{
+ LLRect rect = tooltip_view->getRect();
+ LLRect parent_rect = tooltip_view->getParent()->getRect();
+ S32 left = rect.mLeft;
+ // invert coordinate system for XUI top-left layout
+ S32 top = parent_rect.getHeight() - rect.mTop;
+ if (!tooltip_msg->empty())
+ {
+ tooltip_msg->append("\n");
+ }
+ std::string msg = llformat("%s %d, %d (%d x %d)",
+ tooltip_view->getName().c_str(),
+ left,
+ top,
+ rect.getWidth(),
+ rect.getHeight() );
+ tooltip_msg->append( msg );
+}
+
+bool LLPreviewedFloater::handleToolTip(S32 x, S32 y, MASK mask)
+{
+ if (!sShowRectangles)
+ {
+ return LLFloater::handleToolTip(x, y, mask);
+ }
+
+ S32 screen_x, screen_y;
+ localPointToScreen(x, y, &screen_x, &screen_y);
+ std::string tooltip_msg;
+ LLView* tooltip_view = this;
+ LLView::tree_iterator_t end_it = endTreeDFS();
+ for (LLView::tree_iterator_t it = beginTreeDFS(); it != end_it; ++it)
+ {
+ LLView* viewp = *it;
+ LLRect screen_rect;
+ viewp->localRectToScreen(viewp->getLocalRect(), &screen_rect);
+ if (!(viewp->getVisible()
+ && screen_rect.pointInRect(screen_x, screen_y)))
+ {
+ it.skipDescendants();
+ }
+ // only report xui names for LLUICtrls, not the various container LLViews
+
+ else if (dynamic_cast<LLUICtrl*>(viewp))
+ {
+ // if we are in a new part of the tree (not a descendent of current tooltip_view)
+ // then push the results for tooltip_view and start with a new potential view
+ // NOTE: this emulates visiting only the leaf nodes that meet our criteria
+
+ if (tooltip_view != this
+ && !viewp->hasAncestor(tooltip_view))
+ {
+ append_view_tooltip(tooltip_view, &tooltip_msg);
+ }
+ tooltip_view = viewp;
+ }
+ }
+
+ append_view_tooltip(tooltip_view, &tooltip_msg);
+
+ LLToolTipMgr::instance().show(LLToolTip::Params()
+ .message(tooltip_msg)
+ .max_width(400));
+ return true;
+}
+
+bool LLPreviewedFloater::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ selectElement(this,x,y,0);
+ return true;
+}
+
+// *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:
+// -This selectElement call fills the overlap panel as normal. The element which is "selected" here is actually just an intermediate selection step;
+// what you've really selected is a list of elements: the one you clicked on and everything that overlaps it.
+// -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).
+// This becomes the final selection (as opposed to the intermediate selection that was just made).
+// -Everything else that is currently displayed on the overlap panel should be hidden from view in the previewed floater itself (setVisible(false)).
+// -Subsequent clicks on other elements in the overlap panel (they should still be there) should make other elements the final selection.
+// -On close or on the click of a new button, everything should be shown again and all selection state should be cleared.
+// ~Jacob, 8/08
+bool LLPreviewedFloater::selectElement(LLView* parent, int x, int y, int depth)
+{
+ if(getVisible())
+ {
+ bool handled = false;
+ if(LLFloaterUIPreview::containerType(parent))
+ {
+ for(child_list_const_iter_t child_it = parent->getChildList()->begin(); child_it != parent->getChildList()->end(); ++child_it)
+ {
+ LLView* child = *child_it;
+ S32 local_x = x - child->getRect().mLeft;
+ S32 local_y = y - child->getRect().mBottom;
+ if (child->pointInView(local_x, local_y) &&
+ child->getVisible() &&
+ selectElement(child, x, y, ++depth))
+ {
+ handled = true;
+ break;
+ }
+ }
+ }
+
+ if(!handled)
+ {
+ LLView::sPreviewClickedElement = parent;
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void LLPreviewedFloater::draw()
+{
+ if(NULL != mFloaterUIPreview)
+ {
+ // Set and unset sDrawPreviewHighlights flag so as to avoid using two flags
+ if(mFloaterUIPreview->mHighlightingOverlaps)
+ {
+ LLView::sDrawPreviewHighlights = true;
+ }
+
+ // If we're looking for truncations, draw debug rects for the displayed
+ // floater only.
+ bool old_debug_rects = LLView::sDebugRects;
+ bool old_show_names = LLView::sDebugRectsShowNames;
+ if (sShowRectangles)
+ {
+ LLView::sDebugRects = true;
+ LLView::sDebugRectsShowNames = false;
+ }
+
+ LLFloater::draw();
+
+ LLView::sDebugRects = old_debug_rects;
+ LLView::sDebugRectsShowNames = old_show_names;
+
+ if(mFloaterUIPreview->mHighlightingOverlaps)
+ {
+ LLView::sDrawPreviewHighlights = false;
+ }
+ }
+}
+
+void LLFloaterUIPreview::onClickToggleOverlapping()
+{
+ if(LLView::sHighlightingDiffs)
+ {
+ onClickToggleDiffHighlighting();
+ mToggleHighlightButton->toggleState();
+ }
+ LLView::sPreviewHighlightedElements.clear(); // clear lists first
+
+ S32 width, height;
+ getResizeLimits(&width, &height); // illegal call of non-static member function
+ if(mHighlightingOverlaps)
+ {
+ mHighlightingOverlaps = !mHighlightingOverlaps;
+ // reset list of preview highlighted elements
+ setRect(LLRect(getRect().mLeft,getRect().mTop,getRect().mRight - mOverlapPanel->getRect().getWidth(),getRect().mBottom));
+ setResizeLimits(width - mOverlapPanel->getRect().getWidth(), height);
+ }
+ else
+ {
+ mHighlightingOverlaps = !mHighlightingOverlaps;
+ displayFloater(false,1);
+ setRect(LLRect(getRect().mLeft,getRect().mTop,getRect().mRight + mOverlapPanel->getRect().getWidth(),getRect().mBottom));
+ setResizeLimits(width + mOverlapPanel->getRect().getWidth(), height);
+ }
+ getChildView("overlap_scroll")->setVisible( mHighlightingOverlaps);
+}
+
+void LLFloaterUIPreview::findOverlapsInChildren(LLView* parent)
+{
+ if(parent->getChildCount() == 0 || !containerType(parent)) // if it has no children or isn't a container type, skip it
+ {
+ return;
+ }
+
+ // for every child of the parent
+ for(child_list_const_iter_t child_it = parent->getChildList()->begin(); child_it != parent->getChildList()->end(); ++child_it)
+ {
+ LLView* child = *child_it;
+ if(overlapIgnorable(child))
+ {
+ continue;
+ }
+
+ // for every sibling
+ for(child_list_const_iter_t sibling_it = parent->getChildList()->begin(); sibling_it != parent->getChildList()->end(); ++sibling_it) // for each sibling
+ {
+ LLView* sibling = *sibling_it;
+ if(overlapIgnorable(sibling))
+ {
+ continue;
+ }
+
+ // 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)
+ if(sibling != child && elementOverlap(child, sibling))
+ {
+ mOverlapPanel->mOverlapMap[child].push_back(sibling); // add to the map
+ }
+ }
+ findOverlapsInChildren(child); // recur
+ }
+}
+
+// *HACK: don't overlap with the drag handle and various other elements
+// 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.
+// *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.
+bool LLFloaterUIPreview::overlapIgnorable(LLView* viewp)
+{
+ return NULL != dynamic_cast<LLDragHandle*>(viewp) ||
+ NULL != dynamic_cast<LLViewBorder*>(viewp) ||
+ NULL != dynamic_cast<LLResizeBar*>(viewp);
+}
+
+// *HACK: these are the only two container types as of 8/08, per Richard
+// This is using dynamic casts because there is no object-oriented way to tell which elements are containers.
+bool LLFloaterUIPreview::containerType(LLView* viewp)
+{
+ return NULL != dynamic_cast<LLPanel*>(viewp) || NULL != dynamic_cast<LLLayoutStack*>(viewp);
+}
+
+// Check if two llview's rectangles overlap, with some tolerance
+bool LLFloaterUIPreview::elementOverlap(LLView* view1, LLView* view2)
+{
+ LLSD rec1 = view1->getRect().getValue();
+ LLSD rec2 = view2->getRect().getValue();
+ int tolerance = 2;
+ return (int)rec1[0] <= (int)rec2[2] - tolerance &&
+ (int)rec2[0] <= (int)rec1[2] - tolerance &&
+ (int)rec1[3] <= (int)rec2[1] - tolerance &&
+ (int)rec2[3] <= (int)rec1[1] - tolerance;
+}
+
+void LLOverlapPanel::draw()
+{
+ static const std::string current_selection_text("Current selection: ");
+ static const std::string overlapper_text("Overlapper: ");
+ LLColor4 text_color = LLColor4::grey;
+ gGL.color4fv(text_color.mV);
+
+ if(!LLView::sPreviewClickedElement)
+ {
+ LLUI::translate(5,getRect().getHeight()-20); // translate to top-5,left-5
+ LLView::sDrawPreviewHighlights = false;
+ LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text, 0, 0, 0, text_color,
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
+ }
+ else
+ {
+ OverlapMap::iterator iterExists = mOverlapMap.find(LLView::sPreviewClickedElement);
+ if(iterExists == mOverlapMap.end())
+ {
+ return;
+ }
+
+ std::list<LLView*> overlappers = mOverlapMap[LLView::sPreviewClickedElement];
+ if(overlappers.size() == 0)
+ {
+ LLUI::translate(5,getRect().getHeight()-20); // translate to top-5,left-5
+ LLView::sDrawPreviewHighlights = false;
+ std::string current_selection = std::string(current_selection_text + LLView::sPreviewClickedElement->getName() + " (no elements overlap)");
+ S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(current_selection) + 10;
+ LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection, 0, 0, 0, text_color,
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
+ // widen panel enough to fit this text
+ LLRect rect = getRect();
+ setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
+ return;
+ }
+
+ // recalculate required with and height; otherwise use cached
+ bool need_to_recalculate_bounds = false;
+ if(mLastClickedElement == NULL)
+ {
+ need_to_recalculate_bounds = true;
+ }
+
+ if(NULL == mLastClickedElement)
+ {
+ mLastClickedElement = LLView::sPreviewClickedElement;
+ }
+
+ // recalculate bounds for scroll panel
+ if(need_to_recalculate_bounds || LLView::sPreviewClickedElement->getName() != mLastClickedElement->getName())
+ {
+ // reset panel's rectangle to its default width and height (300x600)
+ LLRect panel_rect = getRect();
+ setRect(LLRect(panel_rect.mLeft,panel_rect.mTop,panel_rect.mLeft+getRect().getWidth(),panel_rect.mTop-getRect().getHeight()));
+
+ LLRect rect;
+
+ // change bounds for selected element
+ int height_sum = mLastClickedElement->getRect().getHeight() + mSpacing + 80;
+ rect = getRect();
+ setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() > mLastClickedElement->getRect().getWidth() + 5 ? rect.mRight : rect.mLeft + mLastClickedElement->getRect().getWidth() + 5, rect.mBottom));
+
+ // and widen to accomodate text if that's wider
+ std::string display_text = current_selection_text + LLView::sPreviewClickedElement->getName();
+ S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(display_text) + 10;
+ rect = getRect();
+ setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
+
+ std::list<LLView*> overlappers = mOverlapMap[LLView::sPreviewClickedElement];
+ for(std::list<LLView*>::iterator overlap_it = overlappers.begin(); overlap_it != overlappers.end(); ++overlap_it)
+ {
+ LLView* viewp = *overlap_it;
+ height_sum += viewp->getRect().getHeight() + mSpacing*3;
+
+ // widen panel's rectangle to accommodate widest overlapping element of this floater
+ rect = getRect();
+ setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() > viewp->getRect().getWidth() + 5 ? rect.mRight : rect.mLeft + viewp->getRect().getWidth() + 5, rect.mBottom));
+
+ // and widen to accomodate text if that's wider
+ std::string display_text = overlapper_text + viewp->getName();
+ S32 text_width = LLFontGL::getFontSansSerifSmall()->getWidth(display_text) + 10;
+ rect = getRect();
+ setRect(LLRect(rect.mLeft,rect.mTop,rect.getWidth() < text_width ? rect.mLeft + text_width : rect.mRight,rect.mTop));
+ }
+ // change panel's height to accommodate all element heights plus spacing between them
+ rect = getRect();
+ setRect(LLRect(rect.mLeft,rect.mTop,rect.mRight,rect.mTop-height_sum));
+ }
+
+ LLUI::translate(5,getRect().getHeight()-10); // translate to top left
+ LLView::sDrawPreviewHighlights = false;
+
+ // draw currently-selected element at top of overlappers
+ LLUI::translate(0,-mSpacing);
+ LLFontGL::getFontSansSerifSmall()->renderUTF8(current_selection_text + LLView::sPreviewClickedElement->getName(), 0, 0, 0, text_color,
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
+ LLUI::translate(0,-mSpacing-LLView::sPreviewClickedElement->getRect().getHeight()); // skip spacing distance + height
+ LLView::sPreviewClickedElement->draw();
+
+ for(std::list<LLView*>::iterator overlap_it = overlappers.begin(); overlap_it != overlappers.end(); ++overlap_it)
+ {
+ LLView* viewp = *overlap_it;
+
+ // draw separating line
+ LLUI::translate(0,-mSpacing);
+ gl_line_2d(0,0,getRect().getWidth()-10,0,LLColor4(192.0f/255.0f,192.0f/255.0f,192.0f/255.0f));
+
+ // draw name
+ LLUI::translate(0,-mSpacing);
+ LLFontGL::getFontSansSerifSmall()->renderUTF8(overlapper_text + viewp->getName(), 0, 0, 0, text_color,
+ LLFontGL::LEFT, LLFontGL::BASELINE, LLFontGL::NORMAL, LLFontGL::NO_SHADOW);
+
+ // draw element
+ LLUI::translate(0,-mSpacing-viewp->getRect().getHeight()); // skip spacing distance + height
+ viewp->draw();
+ }
+ mLastClickedElement = LLView::sPreviewClickedElement;
+ }
+}
+
+void LLFloaterUIPreviewUtil::registerFloater()
+{
+ LLFloaterReg::add("ui_preview", "floater_ui_preview.xml",
+ &LLFloaterReg::build<LLFloaterUIPreview>);
+}