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