diff options
46 files changed, 1185 insertions, 622 deletions
| diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp index 36c5b67826..034c816742 100644 --- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp @@ -240,7 +240,7 @@ void store_input_file(std::list<std::string> &input_filenames, const std::string  		LLDirIterator iter(dir, name);  		while (iter.next(next_name))  		{ -			std::string file_name = dir + gDirUtilp->getDirDelimiter() + next_name; +			std::string file_name = gDirUtilp->add(dir, next_name);  			input_filenames.push_back(file_name);  		}  	} diff --git a/indra/integration_tests/llui_libtest/llui_libtest.cpp b/indra/integration_tests/llui_libtest/llui_libtest.cpp index 217e26c3ca..38aa1bbeb2 100644 --- a/indra/integration_tests/llui_libtest/llui_libtest.cpp +++ b/indra/integration_tests/llui_libtest/llui_libtest.cpp @@ -107,12 +107,6 @@ public:  };  TestImageProvider gTestImageProvider; -static std::string get_xui_dir() -{ -	std::string delim = gDirUtilp->getDirDelimiter(); -	return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim; -} -  void init_llui()  {  	// Font lookup needs directory support @@ -122,13 +116,12 @@ void init_llui()  	const char* newview_path = "../../../newview";  #endif  	gDirUtilp->initAppDirs("SecondLife", newview_path); -	gDirUtilp->setSkinFolder("default"); +	gDirUtilp->setSkinFolder("default", "en");  	// colors are no longer stored in a LLControlGroup file  	LLUIColorTable::instance().loadFromSettings(); -	std::string config_filename = gDirUtilp->getExpandedFilename( -																 LL_PATH_APP_SETTINGS, "settings.xml"); +	std::string config_filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "settings.xml");  	gSavedSettings.loadFromFile(config_filename);  	// See LLAppViewer::init() @@ -143,9 +136,7 @@ void init_llui()  	const bool no_register_widgets = false;  	LLWidgetReg::initClass( no_register_widgets ); -	 -	// Unclear if this is needed -	LLUI::setupPaths(); +  	// Otherwise we get translation warnings when setting up floaters  	// (tooltips for buttons)  	std::set<std::string> default_args; @@ -157,7 +148,6 @@ void init_llui()  	// otherwise it crashes.  	LLFontGL::initClass(96.f, 1.f, 1.f,  						gDirUtilp->getAppRODataDir(), -						LLUI::getXUIPaths(),  						false );	// don't create gl textures  	LLFloaterView::Params fvparams; @@ -169,6 +159,14 @@ void init_llui()  	gFloaterView = LLUICtrlFactory::create<LLFloaterView> (fvparams);  } +/*==========================================================================*| +static std::string get_xui_dir() +{ +	std::string delim = gDirUtilp->getDirDelimiter(); +	return gDirUtilp->getSkinBaseDir() + delim + "default" + delim + "xui" + delim; +} + +// buildFromFile() no longer supports generate-output-LLXMLNode  void export_test_floaters()  {  	// Convert all test floaters to new XML format @@ -191,7 +189,7 @@ void export_test_floaters()  		floater->buildFromFile(	filename,  								//	 FALSE,	// don't open floater  								output_node); -		std::string out_filename = xui_dir + filename; +		std::string out_filename = gDirUtilp->add(xui_dir, filename);  		std::string::size_type extension_pos = out_filename.rfind(".xml");  		out_filename.resize(extension_pos);  		out_filename += "_new.xml"; @@ -203,6 +201,7 @@ void export_test_floaters()  		fclose(floater_file);  	}  } +|*==========================================================================*/  int main(int argc, char** argv)  { @@ -211,7 +210,7 @@ int main(int argc, char** argv)  	init_llui(); -	export_test_floaters(); +//	export_test_floaters();  	return 0;  } diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp index 277f0a5367..991dfd9dce 100644 --- a/indra/linux_updater/linux_updater.cpp +++ b/indra/linux_updater/linux_updater.cpp @@ -251,7 +251,7 @@ std::string next_image_filename(std::string& image_path, LLDirIterator& iter)  {  	std::string image_filename;  	iter.next(image_filename); -	return image_path + "/" + image_filename; +	return gDirUtilp->add(image_path, image_filename);  }  void on_window_closed(GtkWidget *sender, GdkEvent* event, gpointer data) diff --git a/indra/llmath/v3color.h b/indra/llmath/v3color.h index 56cb2ae73e..daf3a6857b 100644 --- a/indra/llmath/v3color.h +++ b/indra/llmath/v3color.h @@ -33,6 +33,7 @@ class LLVector4;  #include "llerror.h"  #include "llmath.h"  #include "llsd.h" +#include <string.h>  //  LLColor3 = |r g b| diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 4dc2fcd714..647512eb2e 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -789,7 +789,7 @@ const LLFontDescriptor& LLFontGL::getFontDesc() const  }  // static -void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector<std::string>& xui_paths, bool create_gl_textures) +void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures)  {  	sVertDPI = (F32)llfloor(screen_dpi * y_scale);  	sHorizDPI = (F32)llfloor(screen_dpi * x_scale); @@ -800,7 +800,7 @@ void LLFontGL::initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::st  	// Font registry init  	if (!sFontRegistry)  	{ -		sFontRegistry = new LLFontRegistry(xui_paths, create_gl_textures); +		sFontRegistry = new LLFontRegistry(create_gl_textures);  		sFontRegistry->parseFontInfo("fonts.xml");  	}  	else diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 5ed5d2c4eb..0988e99deb 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -150,7 +150,7 @@ public:  	const LLFontDescriptor& getFontDesc() const; -	static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, const std::vector<std::string>& xui_paths, bool create_gl_textures = true); +	static void initClass(F32 screen_dpi, F32 x_scale, F32 y_scale, const std::string& app_dir, bool create_gl_textures = true);  	// Load sans-serif, sans-serif-small, etc.  	// Slow, requires multiple seconds to load fonts. diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 4d22eba3d9..b5bdba996f 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -163,14 +163,9 @@ LLFontDescriptor LLFontDescriptor::normalize() const  	return LLFontDescriptor(new_name,new_size,new_style,getFileNames());  } -LLFontRegistry::LLFontRegistry(const string_vec_t& xui_paths, -							   bool create_gl_textures) +LLFontRegistry::LLFontRegistry(bool create_gl_textures)  :	mCreateGLTextures(create_gl_textures)  { -	// Propagate this down from LLUICtrlFactory so LLRender doesn't -	// need an upstream dependency on LLUI. -	mXUIPaths = xui_paths; -	  	// This is potentially a slow directory traversal, so we want to  	// cache the result.  	mUltimateFallbackList = LLWindow::getDynamicFallbackFontList(); @@ -183,27 +178,30 @@ LLFontRegistry::~LLFontRegistry()  bool LLFontRegistry::parseFontInfo(const std::string& xml_filename)  { -	bool success = false;  // Succeed if we find at least one XUI file -	const string_vec_t& xml_paths = mXUIPaths; +	bool success = false;  // Succeed if we find and read at least one XUI file +	const string_vec_t xml_paths = gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_filename); +	if (xml_paths.empty()) +	{ +		// We didn't even find one single XUI file +		return false; +	} +  	for (string_vec_t::const_iterator path_it = xml_paths.begin();  		 path_it != xml_paths.end();  		 ++path_it)  	{ -	  		LLXMLNodePtr root; -		std::string full_filename = gDirUtilp->findSkinnedFilename(*path_it, xml_filename); -		bool parsed_file = LLXMLNode::parseFile(full_filename, root, NULL); +		bool parsed_file = LLXMLNode::parseFile(*path_it, root, NULL);  		if (!parsed_file)  			continue; -		 +  		if ( root.isNull() || ! root->hasName( "fonts" ) )  		{ -			llwarns << "Bad font info file: " -					<< full_filename << llendl; +			llwarns << "Bad font info file: " << *path_it << llendl;  			continue;  		} -		 +  		std::string root_name;  		root->getAttributeString("name",root_name);  		if (root->hasName("fonts")) @@ -215,7 +213,7 @@ bool LLFontRegistry::parseFontInfo(const std::string& xml_filename)  	}  	//if (success)  	//	dump(); -	 +  	return success;  } diff --git a/indra/llrender/llfontregistry.h b/indra/llrender/llfontregistry.h index 8b06191c56..059248fbbd 100644 --- a/indra/llrender/llfontregistry.h +++ b/indra/llrender/llfontregistry.h @@ -67,8 +67,7 @@ class LLFontRegistry  public:  	// create_gl_textures - set to false for test apps with no OpenGL window,  	// such as llui_libtest -	LLFontRegistry(const string_vec_t& xui_paths, -		bool create_gl_textures); +	LLFontRegistry(bool create_gl_textures);  	~LLFontRegistry();  	// Load standard font info from XML file(s). @@ -105,7 +104,6 @@ private:  	font_size_map_t mFontSizes;  	string_vec_t mUltimateFallbackList; -	string_vec_t mXUIPaths;  	bool mCreateGLTextures;  }; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index 90251ac7c6..054b9173d3 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -3233,24 +3233,14 @@ bool LLFloater::isVisible(const LLFloater* floater)  static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters"); -bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_node) +bool LLFloater::buildFromFile(const std::string& filename)  {  	LLFastTimer timer(FTM_BUILD_FLOATERS);  	LLXMLNodePtr root; -	//if exporting, only load the language being exported,  -	//instead of layering localized version on top of english -	if (output_node) -	{ -		if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) -		{ -			llwarns << "Couldn't parse floater from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; -			return false; -		} -	} -	else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) +	if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))  	{ -		llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; +		llwarns << "Couldn't find (or parse) floater from: " << filename << llendl;  		return false;  	} @@ -3275,7 +3265,7 @@ bool LLFloater::buildFromFile(const std::string& filename, LLXMLNodePtr output_n  		getCommitCallbackRegistrar().pushScope();  		getEnableCallbackRegistrar().pushScope(); -		res = initFloaterXML(root, getParent(), filename, output_node); +		res = initFloaterXML(root, getParent(), filename, NULL);  		setXMLFilename(filename); diff --git a/indra/llui/llfloater.h b/indra/llui/llfloater.h index 64d6dcea04..e64b6d04d3 100644 --- a/indra/llui/llfloater.h +++ b/indra/llui/llfloater.h @@ -202,7 +202,7 @@ public:  	// Don't export top/left for rect, only height/width  	static void setupParamsForExport(Params& p, LLView* parent); -	bool buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL); +	bool buildFromFile(const std::string &filename);  	boost::signals2::connection setMinimizeCallback( const commit_signal_t::slot_type& cb );  	boost::signals2::connection setOpenCallback( const commit_signal_t::slot_type& cb ); diff --git a/indra/llui/llfloaterreg.cpp b/indra/llui/llfloaterreg.cpp index 9115eb7174..306caf2b91 100644 --- a/indra/llui/llfloaterreg.cpp +++ b/indra/llui/llfloaterreg.cpp @@ -154,7 +154,7 @@ LLFloater* LLFloaterReg::getInstance(const std::string& name, const LLSD& key)  					llwarns << "Failed to build floater type: '" << name << "'." << llendl;  					return NULL;  				} -				bool success = res->buildFromFile(xui_file, NULL); +				bool success = res->buildFromFile(xui_file);  				if (!success)  				{  					llwarns << "Failed to build floater type: '" << name << "'." << llendl; diff --git a/indra/llui/llnotifications.cpp b/indra/llui/llnotifications.cpp index 629eef2c3b..210a320f41 100644 --- a/indra/llui/llnotifications.cpp +++ b/indra/llui/llnotifications.cpp @@ -1424,25 +1424,19 @@ void addPathIfExists(const std::string& new_path, std::vector<std::string>& path  bool LLNotifications::loadTemplates()  {  	llinfos << "Reading notifications template" << llendl; -	std::vector<std::string> search_paths; -	 -	std::string skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml"; -	std::string localized_skin_relative_path = gDirUtilp->getDirDelimiter() + LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + "notifications.xml"; - -	addPathIfExists(gDirUtilp->getDefaultSkinDir() + skin_relative_path, search_paths); -	addPathIfExists(gDirUtilp->getDefaultSkinDir() + localized_skin_relative_path, search_paths); -	addPathIfExists(gDirUtilp->getSkinDir() + skin_relative_path, search_paths); -	addPathIfExists(gDirUtilp->getSkinDir() + localized_skin_relative_path, search_paths); -	addPathIfExists(gDirUtilp->getUserSkinDir() + skin_relative_path, search_paths); -	addPathIfExists(gDirUtilp->getUserSkinDir() + localized_skin_relative_path, search_paths); +	// Passing findSkinnedFilenames(constraint=LLDir::ALL_SKINS) makes it +	// output all relevant pathnames instead of just the ones from the most +	// specific skin. +	std::vector<std::string> search_paths = +		gDirUtilp->findSkinnedFilenames(LLDir::XUI, "notifications.xml", LLDir::ALL_SKINS);  	std::string base_filename = search_paths.front();  	LLXMLNodePtr root;  	BOOL success  = LLXMLNode::getLayeredXMLNode(root, search_paths); -	 +  	if (!success || root.isNull() || !root->hasName( "notifications" ))  	{ -		llerrs << "Problem reading UI Notifications file: " << base_filename << llendl; +		llerrs << "Problem reading XML from UI Notifications file: " << base_filename << llendl;  		return false;  	} @@ -1452,7 +1446,7 @@ bool LLNotifications::loadTemplates()  	if(!params.validateBlock())  	{ -		llerrs << "Problem reading UI Notifications file: " << base_filename << llendl; +		llerrs << "Problem reading XUI from UI Notifications file: " << base_filename << llendl;  		return false;  	} @@ -1508,7 +1502,9 @@ bool LLNotifications::loadTemplates()  bool LLNotifications::loadVisibilityRules()  {  	const std::string xml_filename = "notification_visibility.xml"; -	std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), xml_filename); +	// Note that here we're looking for the "en" version, the default +	// language, rather than the most localized version of this file. +	std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, xml_filename);  	LLNotificationVisibilityRule::Rules params;  	LLSimpleXUIParser parser; diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 00318cec6b..67472ad166 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -968,25 +968,15 @@ static LLFastTimer::DeclareTimer FTM_BUILD_PANELS("Build Panels");  //-----------------------------------------------------------------------------  // buildPanel()  //----------------------------------------------------------------------------- -BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_node, const LLPanel::Params& default_params) +BOOL LLPanel::buildFromFile(const std::string& filename, const LLPanel::Params& default_params)  {  	LLFastTimer timer(FTM_BUILD_PANELS);  	BOOL didPost = FALSE;  	LLXMLNodePtr root; -	//if exporting, only load the language being exported,  -	//instead of layering localized version on top of english -	if (output_node) -	{	 -		if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root)) -		{ -			llwarns << "Couldn't parse panel from: " << LLUI::getLocalizedSkinPath() + gDirUtilp->getDirDelimiter() + filename  << llendl; -			return didPost; -		} -	} -	else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root)) +	if (!LLUICtrlFactory::getLayeredXMLNode(filename, root))  	{ -		llwarns << "Couldn't parse panel from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl; +		llwarns << "Couldn't parse panel from: " << filename << llendl;  		return didPost;  	} @@ -1010,7 +1000,7 @@ BOOL LLPanel::buildFromFile(const std::string& filename, LLXMLNodePtr output_nod  		getCommitCallbackRegistrar().pushScope();  		getEnableCallbackRegistrar().pushScope(); -		didPost = initPanelXML(root, NULL, output_node, default_params); +		didPost = initPanelXML(root, NULL, NULL, default_params);  		getCommitCallbackRegistrar().popScope();  		getEnableCallbackRegistrar().popScope(); diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index f620201020..e63b41f97c 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -105,7 +105,7 @@ protected:  	LLPanel(const LLPanel::Params& params = getDefaultParams());  public: -	BOOL buildFromFile(const std::string &filename, LLXMLNodePtr output_node = NULL, const LLPanel::Params&default_params = getDefaultParams()); +	BOOL buildFromFile(const std::string &filename, const LLPanel::Params& default_params = getDefaultParams());  	static LLPanel* createFactoryPanel(const std::string& name); diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp index 58fa8a0828..80d079cbc8 100644 --- a/indra/llui/lltransutil.cpp +++ b/indra/llui/lltransutil.cpp @@ -31,15 +31,20 @@  #include "lltrans.h"  #include "lluictrlfactory.h"  #include "llxmlnode.h" - +#include "lldir.h"  bool LLTransUtil::parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args)  {  	LLXMLNodePtr root; -	BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root); +	// Pass LLDir::ALL_SKINS to load a composite of all the individual string +	// definitions in the default skin and the current skin. This means an +	// individual skin can provide an xml_filename that overrides only a +	// subset of the available string definitions; any string definition not +	// overridden by that skin will be sought in the default skin. +	bool success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root, LLDir::ALL_SKINS);  	if (!success)  	{ -		llerrs << "Couldn't load string table" << llendl; +		llerrs << "Couldn't load string table " << xml_filename << llendl;  		return false;  	} @@ -54,7 +59,7 @@ bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)  	if (!success)  	{ -		llerrs << "Couldn't load string table " << xml_filename << llendl; +		llerrs << "Couldn't load localization table " << xml_filename << llendl;  		return false;  	} diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index 87bf518aa1..6d2bc1837c 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -1836,88 +1836,37 @@ struct Paths : public LLInitParam::Block<Paths>  	{}  }; -//static -void LLUI::setupPaths() -{ -	std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "paths.xml"); - -	LLXMLNodePtr root; -	BOOL success  = LLXMLNode::parseFile(filename, root, NULL); -	Paths paths; - -	if(success) -	{ -		LLXUIParser parser; -		parser.readXUI(root, paths, filename); -	} -	sXUIPaths.clear(); -	 -	if (success && paths.validateBlock()) -	{ -		LLStringUtil::format_map_t path_args; -		path_args["[LANGUAGE]"] = LLUI::getLanguage(); -		 -		for (LLInitParam::ParamIterator<Directory>::const_iterator it = paths.directories.begin(),  -				end_it = paths.directories.end(); -			it != end_it; -			++it) -		{ -			std::string path_val_ui; -			for (LLInitParam::ParamIterator<SubDir>::const_iterator subdir_it = it->subdirs.begin(), -					subdir_end_it = it->subdirs.end(); -				subdir_it != subdir_end_it;) -			{ -				path_val_ui += subdir_it->value(); -				if (++subdir_it != subdir_end_it) -					path_val_ui += gDirUtilp->getDirDelimiter(); -			} -			LLStringUtil::format(path_val_ui, path_args); -			if (std::find(sXUIPaths.begin(), sXUIPaths.end(), path_val_ui) == sXUIPaths.end()) -			{ -				sXUIPaths.push_back(path_val_ui); -			} - -		} -	} -	else // parsing failed -	{ -		std::string slash = gDirUtilp->getDirDelimiter(); -		std::string dir = "xui" + slash + "en"; -		llwarns << "XUI::config file unable to open: " << filename << llendl; -		sXUIPaths.push_back(dir); -	} -} -  //static  std::string LLUI::locateSkin(const std::string& filename)  { -	std::string slash = gDirUtilp->getDirDelimiter();  	std::string found_file = filename; -	if (!gDirUtilp->fileExists(found_file)) +	if (gDirUtilp->fileExists(found_file))  	{ -		found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? +		return found_file;  	} -	if (sSettingGroups["config"] && sSettingGroups["config"]->controlExists("Language")) + +	found_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename); // Should be CUSTOM_SKINS? +	if (gDirUtilp->fileExists(found_file))  	{ -		if (!gDirUtilp->fileExists(found_file)) -		{ -			std::string localization = getLanguage(); -			std::string local_skin = "xui" + slash + localization + slash + filename; -			found_file = gDirUtilp->findSkinnedFilename(local_skin); -		} +		return found_file;  	} -	if (!gDirUtilp->fileExists(found_file)) + +	found_file = gDirUtilp->findSkinnedFilename(LLDir::XUI, filename); +	if (! found_file.empty())  	{ -		std::string local_skin = "xui" + slash + "en" + slash + filename; -		found_file = gDirUtilp->findSkinnedFilename(local_skin); +		return found_file;  	} -	if (!gDirUtilp->fileExists(found_file)) + +	found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); +	if (gDirUtilp->fileExists(found_file))  	{ -		found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename); +		return found_file;  	} -	return found_file; -}	 +	LL_WARNS("LLUI") << "Can't find '" << filename +					 << "' in user settings, any skin directory or app_settings" << LL_ENDL; +	return ""; +}  //static  LLVector2 LLUI::getWindowSize() diff --git a/indra/llui/llui.h b/indra/llui/llui.h index 28e84fa444..c5a12d2b31 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -292,11 +292,6 @@ public:  	// Return the ISO639 language name ("en", "ko", etc.) for the viewer UI.  	// http://www.loc.gov/standards/iso639-2/php/code_list.php  	static std::string getLanguage(); -	 -	static void setupPaths(); -	static const std::vector<std::string>& getXUIPaths() { return sXUIPaths; } -	static std::string getSkinPath() { return sXUIPaths.front(); } -	static std::string getLocalizedSkinPath() { return sXUIPaths.back(); }  //all files may not exist at the localized path  	//helper functions (should probably move free standing rendering helper functions here)  	static LLView* getRootView() { return sRootView; } diff --git a/indra/llui/lluicolortable.cpp b/indra/llui/lluicolortable.cpp index 9455d09cc0..ffeff15968 100644 --- a/indra/llui/lluicolortable.cpp +++ b/indra/llui/lluicolortable.cpp @@ -32,6 +32,7 @@  #include "llui.h"  #include "lluicolortable.h"  #include "lluictrlfactory.h" +#include <boost/foreach.hpp>  LLUIColorTable::ColorParams::ColorParams()  :	value("value"), @@ -206,19 +207,12 @@ bool LLUIColorTable::loadFromSettings()  {  	bool result = false; -	std::string default_filename = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "colors.xml"); -	result |= loadFromFilename(default_filename, mLoadedColors); - -	std::string current_filename = gDirUtilp->getExpandedFilename(LL_PATH_TOP_SKIN, "colors.xml"); -	if(current_filename != default_filename) -	{ -		result |= loadFromFilename(current_filename, mLoadedColors); -	} - -	current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml"); -	if(current_filename != default_filename) +	// pass constraint=LLDir::ALL_SKINS because we want colors.xml from every +	// skin dir +	BOOST_FOREACH(std::string colors_path, +				  gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS))  	{ -		result |= loadFromFilename(current_filename, mLoadedColors); +		result |= loadFromFilename(colors_path, mLoadedColors);  	}  	std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml"); diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 25e7a31e90..bd06476936 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -90,10 +90,12 @@ LLUICtrlFactory::~LLUICtrlFactory()  void LLUICtrlFactory::loadWidgetTemplate(const std::string& widget_tag, LLInitParam::BaseBlock& block)  { -	std::string filename = std::string("widgets") + gDirUtilp->getDirDelimiter() + widget_tag + ".xml"; +	std::string filename = gDirUtilp->add("widgets", widget_tag + ".xml");  	LLXMLNodePtr root_node; -	std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getXUIPaths().front(), filename); +	// Here we're looking for the "en" version, the default-language version +	// of the file, rather than the localized version. +	std::string full_filename = gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, filename);  	if (!full_filename.empty())  	{  		LLUICtrlFactory::instance().pushFileName(full_filename); @@ -149,22 +151,12 @@ static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing");  //-----------------------------------------------------------------------------  // getLayeredXMLNode()  //----------------------------------------------------------------------------- -bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) +bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root, +                                        LLDir::ESkinConstraint constraint)  {  	LLFastTimer timer(FTM_XML_PARSE); -	 -	std::vector<std::string> paths; -	std::string path = gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), xui_filename); -	if (!path.empty()) -	{ -		paths.push_back(path); -	} - -	std::string localize_path = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename); -	if (!localize_path.empty() && localize_path != path) -	{ -		paths.push_back(localize_path); -	} +	std::vector<std::string> paths = +		gDirUtilp->findSkinnedFilenames(LLDir::XUI, xui_filename, constraint);  	if (paths.empty())  	{ @@ -177,23 +169,6 @@ bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNo  //----------------------------------------------------------------------------- -// getLocalizedXMLNode() -//----------------------------------------------------------------------------- -bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root) -{ -	LLFastTimer timer(FTM_XML_PARSE); -	std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), xui_filename); -	if (!LLXMLNode::parseFile(full_filename, root, NULL)) -	{ -		return false; -	} -	else -	{ -		return true; -	} -} - -//-----------------------------------------------------------------------------  // saveToXML()  //-----------------------------------------------------------------------------  S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename) @@ -239,8 +214,10 @@ std::string LLUICtrlFactory::getCurFileName()  void LLUICtrlFactory::pushFileName(const std::string& name)  -{  -	mFileNames.push_back(gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), name));  +{ +	// Here we seem to be looking for the default language file ("en") rather +	// than the localized one, if any. +	mFileNames.push_back(gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, name));  }  void LLUICtrlFactory::popFileName()  @@ -255,14 +232,6 @@ void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group)  	parent->addChild(view, tab_group);  } - -// Avoid directly using LLUI and LLDir in the template code -//static -std::string LLUICtrlFactory::findSkinnedFilename(const std::string& filename) -{ -	return gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename); -} -  //static   void LLUICtrlFactory::copyName(LLXMLNodePtr src, LLXMLNodePtr dest)  { diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h index 4e54354731..f6971261d7 100644 --- a/indra/llui/lluictrlfactory.h +++ b/indra/llui/lluictrlfactory.h @@ -32,6 +32,7 @@  #include "llregistry.h"  #include "llxuiparser.h"  #include "llstl.h" +#include "lldir.h"  class LLView; @@ -161,32 +162,21 @@ public:  	LLView* createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t&, LLXMLNodePtr output_node );  	template<typename T> -	static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry, LLXMLNodePtr output_node = NULL) +	static T* createFromFile(const std::string &filename, LLView *parent, const widget_registry_t& registry)  	{  		T* widget = NULL; -		 -		std::string skinned_filename = findSkinnedFilename(filename); +  		instance().pushFileName(filename);  		{  			LLXMLNodePtr root_node; -			//if exporting, only load the language being exported, 			 -			//instead of layering localized version on top of english			 -			if (output_node)			 -			{					 -				if (!LLUICtrlFactory::getLocalizedXMLNode(filename, root_node))				 -				{							 -					llwarns << "Couldn't parse XUI file: " <<  filename  << llendl;					 -					goto fail;				 -				} -			} -			else if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node)) +			if (!LLUICtrlFactory::getLayeredXMLNode(filename, root_node))  			{ -				llwarns << "Couldn't parse XUI file: " << skinned_filename << llendl; +				llwarns << "Couldn't parse XUI file: " << instance().getCurFileName() << llendl;  				goto fail;  			} -			 -			LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, output_node); + +			LLView* view = getInstance()->createFromXML(root_node, parent, filename, registry, NULL);  			if (view)  			{  				widget = dynamic_cast<T*>(view); @@ -214,8 +204,8 @@ fail:  	static void createChildren(LLView* viewp, LLXMLNodePtr node, const widget_registry_t&, LLXMLNodePtr output_node = NULL); -	static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root); -	static bool getLocalizedXMLNode(const std::string &xui_filename, LLXMLNodePtr& root); +	static bool getLayeredXMLNode(const std::string &filename, LLXMLNodePtr& root, +								  LLDir::ESkinConstraint constraint=LLDir::CURRENT_SKIN);  private:  	//NOTE: both friend declarations are necessary to keep both gcc and msvc happy @@ -300,9 +290,6 @@ private:  	// this exists to get around dependency on llview  	static void setCtrlParent(LLView* view, LLView* parent, S32 tab_group); -	// Avoid directly using LLUI and LLDir in the template code -	static std::string findSkinnedFilename(const std::string& filename); -  	class LLPanel*		mDummyPanel;  	std::vector<std::string>	mFileNames;  }; diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 32d081d552..5e5aeefba1 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -41,6 +41,17 @@  #include "lluuid.h"  #include "lldiriterator.h" +#include "stringize.h" +#include <boost/foreach.hpp> +#include <boost/range/begin.hpp> +#include <boost/range/end.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> +#include <boost/ref.hpp> +#include <algorithm> + +using boost::assign::list_of; +using boost::assign::map_list_of;  #if LL_WINDOWS  #include "lldir_win32.h" @@ -58,6 +69,14 @@ LLDir_Linux gDirUtil;  LLDir *gDirUtilp = (LLDir *)&gDirUtil; +/// Values for findSkinnedFilenames(subdir) parameter +const char +	*LLDir::XUI      = "xui", +	*LLDir::TEXTURES = "textures", +	*LLDir::SKINBASE = ""; + +static const char* const empty = ""; +  LLDir::LLDir()  :	mAppName(""),  	mExecutablePathAndName(""), @@ -70,7 +89,8 @@ LLDir::LLDir()  	mOSCacheDir(""),  	mCAFile(""),  	mTempDir(""), -	mDirDelimiter("/") // fallback to forward slash if not overridden +	mDirDelimiter("/"), // fallback to forward slash if not overridden +	mLanguage("en")  {  } @@ -96,9 +116,7 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask)  	LLDirIterator iter(dirname, mask);  	while (iter.next(filename))  	{ -		fullpath = dirname; -		fullpath += getDirDelimiter(); -		fullpath += filename; +		fullpath = add(dirname, filename);  		if(LLFile::isdir(fullpath))  		{ @@ -270,12 +288,12 @@ std::string LLDir::buildSLOSCacheDir() const  		}  		else  		{ -			res = getOSUserAppDir() + mDirDelimiter + "cache"; +			res = add(getOSUserAppDir(), "cache");  		}  	}  	else  	{ -		res = getOSCacheDir() + mDirDelimiter + "SecondLife"; +		res = add(getOSCacheDir(), "SecondLife");  	}  	return res;  } @@ -298,19 +316,24 @@ const std::string &LLDir::getDirDelimiter() const  	return mDirDelimiter;  } +const std::string& LLDir::getDefaultSkinDir() const +{ +	return mDefaultSkinDir; +} +  const std::string &LLDir::getSkinDir() const  {  	return mSkinDir;  } -const std::string &LLDir::getUserSkinDir() const +const std::string &LLDir::getUserDefaultSkinDir() const  { -	return mUserSkinDir; +    return mUserDefaultSkinDir;  } -const std::string& LLDir::getDefaultSkinDir() const +const std::string &LLDir::getUserSkinDir() const  { -	return mDefaultSkinDir; +	return mUserSkinDir;  }  const std::string LLDir::getSkinBaseDir() const @@ -323,6 +346,39 @@ const std::string &LLDir::getLLPluginDir() const  	return mLLPluginDir;  } +static std::string ELLPathToString(ELLPath location) +{ +	typedef std::map<ELLPath, const char*> ELLPathMap; +#define ENT(symbol) (symbol, #symbol) +	static const ELLPathMap sMap = map_list_of +		ENT(LL_PATH_NONE) +		ENT(LL_PATH_USER_SETTINGS) +		ENT(LL_PATH_APP_SETTINGS) +		ENT(LL_PATH_PER_SL_ACCOUNT) // returns/expands to blank string if we don't know the account name yet +		ENT(LL_PATH_CACHE) +		ENT(LL_PATH_CHARACTER) +		ENT(LL_PATH_HELP) +		ENT(LL_PATH_LOGS) +		ENT(LL_PATH_TEMP) +		ENT(LL_PATH_SKINS) +		ENT(LL_PATH_TOP_SKIN) +		ENT(LL_PATH_CHAT_LOGS) +		ENT(LL_PATH_PER_ACCOUNT_CHAT_LOGS) +		ENT(LL_PATH_USER_SKIN) +		ENT(LL_PATH_LOCAL_ASSETS) +		ENT(LL_PATH_EXECUTABLE) +		ENT(LL_PATH_DEFAULT_SKIN) +		ENT(LL_PATH_FONTS) +		ENT(LL_PATH_LAST) +	; +#undef ENT + +	ELLPathMap::const_iterator found = sMap.find(location); +	if (found != sMap.end()) +		return found->second; +	return STRINGIZE("Invalid ELLPath value " << location); +} +  std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const  {  	return getExpandedFilename(location, "", filename); @@ -343,15 +399,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd  		break;  	case LL_PATH_APP_SETTINGS: -		prefix = getAppRODataDir(); -		prefix += mDirDelimiter; -		prefix += "app_settings"; +		prefix = add(getAppRODataDir(), "app_settings");  		break;  	case LL_PATH_CHARACTER: -		prefix = getAppRODataDir(); -		prefix += mDirDelimiter; -		prefix += "character"; +		prefix = add(getAppRODataDir(), "character");  		break;  	case LL_PATH_HELP: @@ -363,16 +415,22 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd  		break;  	case LL_PATH_USER_SETTINGS: -		prefix = getOSUserAppDir(); -		prefix += mDirDelimiter; -		prefix += "user_settings"; +		prefix = add(getOSUserAppDir(), "user_settings");  		break;  	case LL_PATH_PER_SL_ACCOUNT:  		prefix = getLindenUserDir();  		if (prefix.empty())  		{ -			// if we're asking for the per-SL-account directory but we haven't logged in yet (or otherwise don't know the account name from which to build this string), then intentionally return a blank string to the caller and skip the below warning about a blank prefix. +			// if we're asking for the per-SL-account directory but we haven't +			// logged in yet (or otherwise don't know the account name from +			// which to build this string), then intentionally return a blank +			// string to the caller and skip the below warning about a blank +			// prefix. +			LL_DEBUGS("LLDir") << "getLindenUserDir() not yet set: " +							   << ELLPathToString(location) +							   << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename +							   << "' => ''" << LL_ENDL;  			return std::string();  		}  		break; @@ -386,9 +444,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd  		break;  	case LL_PATH_LOGS: -		prefix = getOSUserAppDir(); -		prefix += mDirDelimiter; -		prefix += "logs"; +		prefix = add(getOSUserAppDir(), "logs");  		break;  	case LL_PATH_TEMP: @@ -412,9 +468,7 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd  		break;  	case LL_PATH_LOCAL_ASSETS: -		prefix = getAppRODataDir(); -		prefix += mDirDelimiter; -		prefix += "local_assets"; +		prefix = add(getAppRODataDir(), "local_assets");  		break;  	case LL_PATH_EXECUTABLE: @@ -422,56 +476,36 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd  		break;  	case LL_PATH_FONTS: -		prefix = getAppRODataDir(); -		prefix += mDirDelimiter; -		prefix += "fonts"; +		prefix = add(getAppRODataDir(), "fonts");  		break;  	default:  		llassert(0);  	} -	std::string filename = in_filename; -	if (!subdir2.empty()) -	{ -		filename = subdir2 + mDirDelimiter + filename; -	} - -	if (!subdir1.empty()) -	{ -		filename = subdir1 + mDirDelimiter + filename; -	} -  	if (prefix.empty())  	{ -		llwarns << "prefix is empty, possible bad filename" << llendl; -	} -	 -	std::string expanded_filename; -	if (!filename.empty()) -	{ -		if (!prefix.empty()) -		{ -			expanded_filename += prefix; -			expanded_filename += mDirDelimiter; -			expanded_filename += filename; -		} -		else -		{ -			expanded_filename = filename; -		} -	} -	else if (!prefix.empty()) -	{ -		// Directory only, no file name. -		expanded_filename = prefix; +		llwarns << ELLPathToString(location) +				<< ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename +				<< "': prefix is empty, possible bad filename" << llendl;  	} -	else + +	std::string expanded_filename = add(add(prefix, subdir1), subdir2); +	if (expanded_filename.empty() && in_filename.empty())  	{ -		expanded_filename.assign(""); +		return "";  	} - -	//llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl; +	// Use explicit concatenation here instead of another add() call. Callers +	// passing in_filename as "" expect to obtain a pathname ending with +	// mDirSeparator so they can later directly concatenate with a specific +	// filename. A caller using add() doesn't care, but there's still code +	// loose in the system that uses std::string::operator+(). +	expanded_filename += mDirDelimiter; +	expanded_filename += in_filename; + +	LL_DEBUGS("LLDir") << ELLPathToString(location) +					   << ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename +					   << "' => '" << expanded_filename << "'" << LL_ENDL;  	return expanded_filename;  } @@ -511,31 +545,207 @@ std::string LLDir::getExtension(const std::string& filepath) const  	return exten;  } -std::string LLDir::findSkinnedFilename(const std::string &filename) const +std::string LLDir::findSkinnedFilenameBaseLang(const std::string &subdir, +											   const std::string &filename, +											   ESkinConstraint constraint) const  { -	return findSkinnedFilename("", "", filename); +	// This implementation is basically just as described in the declaration comments. +	std::vector<std::string> found(findSkinnedFilenames(subdir, filename, constraint)); +	if (found.empty()) +	{ +		return ""; +	} +	return found.front();  } -std::string LLDir::findSkinnedFilename(const std::string &subdir, const std::string &filename) const +std::string LLDir::findSkinnedFilename(const std::string &subdir, +									   const std::string &filename, +									   ESkinConstraint constraint) const  { -	return findSkinnedFilename("", subdir, filename); +	// This implementation is basically just as described in the declaration comments. +	std::vector<std::string> found(findSkinnedFilenames(subdir, filename, constraint)); +	if (found.empty()) +	{ +		return ""; +	} +	return found.back();  } -std::string LLDir::findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const +// This method exists because the two code paths for +// findSkinnedFilenames(ALL_SKINS) and findSkinnedFilenames(CURRENT_SKIN) must +// generate the list of candidate pathnames in identical ways. The only +// difference is in the body of the inner loop. +template <typename FUNCTION> +void LLDir::walkSearchSkinDirs(const std::string& subdir, +							   const std::vector<std::string>& subsubdirs, +							   const std::string& filename, +							   const FUNCTION& function) const  { -	// generate subdirectory path fragment, e.g. "/foo/bar", "/foo", "" -	std::string subdirs = ((subdir1.empty() ? "" : mDirDelimiter) + subdir1) -						 + ((subdir2.empty() ? "" : mDirDelimiter) + subdir2); +	BOOST_FOREACH(std::string skindir, mSearchSkinDirs) +	{ +		std::string subdir_path(add(skindir, subdir)); +		BOOST_FOREACH(std::string subsubdir, subsubdirs) +		{ +			std::string full_path(add(add(subdir_path, subsubdir), filename)); +			if (fileExists(full_path)) +			{ +				function(subsubdir, full_path); +			} +		} +	} +} -	std::vector<std::string> search_paths; -	 -	search_paths.push_back(getUserSkinDir() + subdirs);		// first look in user skin override -	search_paths.push_back(getSkinDir() + subdirs);			// then in current skin -	search_paths.push_back(getDefaultSkinDir() + subdirs);  // then default skin -	search_paths.push_back(getCacheDir() + subdirs);		// and last in preload directory +// ridiculous little helper function that should go away when we can use lambda +inline void push_back(std::vector<std::string>& vector, const std::string& value) +{ +	vector.push_back(value); +} + +typedef std::map<std::string, std::string> StringMap; +// ridiculous little helper function that should go away when we can use lambda +inline void store_in_map(StringMap& map, const std::string& key, const std::string& value) +{ +	map[key] = value; +} + +std::vector<std::string> LLDir::findSkinnedFilenames(const std::string& subdir, +													 const std::string& filename, +													 ESkinConstraint constraint) const +{ +	// Recognize subdirs that have no localization. +	static const std::set<std::string> sUnlocalized = list_of +		("")                        // top-level directory not localized +		("textures")                // textures not localized +	; + +	LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename +					   << "', constraint " +					   << ((constraint == CURRENT_SKIN)? "CURRENT_SKIN" : "ALL_SKINS") +					   << LL_ENDL; + +	// Cache the default language directory for each subdir we've encountered. +	// A cache entry whose value is the empty string means "not localized, +	// don't bother checking again." +	static StringMap sLocalized; + +	// Check whether we've already discovered if this subdir is localized. +	StringMap::const_iterator found = sLocalized.find(subdir); +	if (found == sLocalized.end()) +	{ +		// We have not yet determined that. Is it one of the subdirs "known" +		// to be unlocalized? +		if (sUnlocalized.find(subdir) != sUnlocalized.end()) +		{ +			// This subdir is known to be unlocalized. Remember that. +			found = sLocalized.insert(StringMap::value_type(subdir, "")).first; +		} +		else +		{ +			// We do not recognize this subdir. Investigate. +			std::string subdir_path(add(getDefaultSkinDir(), subdir)); +			if (fileExists(add(subdir_path, "en"))) +			{ +				// defaultSkinDir/subdir contains subdir "en". That's our +				// default language; this subdir is localized. +				found = sLocalized.insert(StringMap::value_type(subdir, "en")).first; +			} +			else if (fileExists(add(subdir_path, "en-us"))) +			{ +				// defaultSkinDir/subdir contains subdir "en-us" but not "en". +				// Set as default language; this subdir is localized. +				found = sLocalized.insert(StringMap::value_type(subdir, "en-us")).first; +			} +			else +			{ +				// defaultSkinDir/subdir contains neither "en" nor "en-us". +				// Assume it's not localized. Remember that assumption. +				found = sLocalized.insert(StringMap::value_type(subdir, "")).first; +			} +		} +	} +	// Every code path above should have resulted in 'found' becoming a valid +	// iterator to an entry in sLocalized. +	llassert(found != sLocalized.end()); + +	// Now -- is this subdir localized, or not? The answer determines what +	// subdirectories we check (under subdir) for the requested filename. +	std::vector<std::string> subsubdirs; +	if (found->second.empty()) +	{ +		// subdir is not localized. filename should be located directly within it. +		subsubdirs.push_back(""); +	} +	else +	{ +		// subdir is localized, and found->second is the default language +		// directory within it. Check both the default language and the +		// current language -- if it differs from the default, of course. +		subsubdirs.push_back(found->second); +		if (mLanguage != found->second) +		{ +			subsubdirs.push_back(mLanguage); +		} +	} + +	// Build results vector. +	std::vector<std::string> results; +	// The process we use depends on 'constraint'. +	if (constraint != CURRENT_SKIN) // meaning ALL_SKINS +	{ +		// ALL_SKINS is simpler: just return every pathname generated by +		// walkSearchSkinDirs(). Tricky bit: walkSearchSkinDirs() passes its +		// FUNCTION the subsubdir as well as the full pathname. We just want +		// the full pathname. +		walkSearchSkinDirs(subdir, subsubdirs, filename, +						   boost::bind(push_back, boost::ref(results), _2)); +	} +	else                            // CURRENT_SKIN +	{ +		// CURRENT_SKIN turns out to be a bit of a misnomer because we might +		// still return files from two different skins. In any case, this +		// value of 'constraint' means we will return at most two paths: one +		// for the default language, one for the current language (supposing +		// those differ). +		// It is important to allow a user to override only the localization +		// for a particular file, for all viewer installs, without also +		// overriding the default-language file. +		// It is important to allow a user to override only the default- +		// language file, for all viewer installs, without also overriding the +		// applicable localization of that file. +		// Therefore, treat the default language and the current language as +		// two separate cases. For each, capture the most-specialized file +		// that exists. +		// Use a map keyed by subsubdir (i.e. language code). This allows us +		// to handle the case of a single subsubdirs entry with the same logic +		// that handles two. For every real file path generated by +		// walkSearchSkinDirs(), update the map entry for its subsubdir. +		StringMap path_for; +		walkSearchSkinDirs(subdir, subsubdirs, filename, +						   boost::bind(store_in_map, boost::ref(path_for), _1, _2)); +		// Now that we have a path for each of the default language and the +		// current language, copy them -- in proper order -- into results. +		// Don't drive this by walking the map itself: it matters that we +		// generate results in the same order as subsubdirs. +		BOOST_FOREACH(std::string subsubdir, subsubdirs) +		{ +			StringMap::const_iterator found(path_for.find(subsubdir)); +			if (found != path_for.end()) +			{ +				results.push_back(found->second); +			} +		} +	} -	std::string found_file = findFile(filename, search_paths); -	return found_file; +	LL_DEBUGS("LLDir") << empty; +	const char* comma = ""; +	BOOST_FOREACH(std::string path, results) +	{ +		LL_CONT << comma << "'" << path << "'"; +		comma = ", "; +	} +	LL_CONT << LL_ENDL; + +	return results;  }  std::string LLDir::getTempFilename() const @@ -546,12 +756,7 @@ std::string LLDir::getTempFilename() const  	random_uuid.generate();  	random_uuid.toString(uuid_str); -	std::string temp_filename = getTempDir(); -	temp_filename += mDirDelimiter; -	temp_filename += uuid_str; -	temp_filename += ".tmp"; - -	return temp_filename; +	return add(getTempDir(), uuid_str + ".tmp");  }  // static @@ -587,9 +792,7 @@ void LLDir::setLindenUserDir(const std::string &username)  		std::string userlower(username);  		LLStringUtil::toLower(userlower);  		LLStringUtil::replaceChar(userlower, ' ', '_'); -		mLindenUserDir = getOSUserAppDir(); -		mLindenUserDir += mDirDelimiter; -		mLindenUserDir += userlower; +		mLindenUserDir = add(getOSUserAppDir(), userlower);  	}  	else  	{ @@ -621,9 +824,7 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username)  		std::string userlower(username);  		LLStringUtil::toLower(userlower);  		LLStringUtil::replaceChar(userlower, ' ', '_'); -		mPerAccountChatLogsDir = getChatLogsDir(); -		mPerAccountChatLogsDir += mDirDelimiter; -		mPerAccountChatLogsDir += userlower; +		mPerAccountChatLogsDir = add(getChatLogsDir(), userlower);  	}  	else  	{ @@ -632,25 +833,59 @@ void LLDir::setPerAccountChatLogsDir(const std::string &username)  } -void LLDir::setSkinFolder(const std::string &skin_folder) +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language)  { -	mSkinDir = getSkinBaseDir(); -	mSkinDir += mDirDelimiter; -	mSkinDir += skin_folder; +	LL_DEBUGS("LLDir") << "Setting skin '" << skin_folder << "', language '" << language << "'" +					   << LL_ENDL; +	mSkinName = skin_folder; +	mLanguage = language; -	// user modifications to current skin -	// e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle -	mUserSkinDir = getOSUserAppDir(); -	mUserSkinDir += mDirDelimiter; -	mUserSkinDir += "skins"; -	mUserSkinDir += mDirDelimiter;	 -	mUserSkinDir += skin_folder; +	// This method is called multiple times during viewer initialization. Each +	// time it's called, reset mSearchSkinDirs. +	mSearchSkinDirs.clear();  	// base skin which is used as fallback for all skinned files  	// e.g. c:\program files\secondlife\skins\default  	mDefaultSkinDir = getSkinBaseDir(); -	mDefaultSkinDir += mDirDelimiter;	 -	mDefaultSkinDir += "default"; +	append(mDefaultSkinDir, "default"); +	// This is always the most general of the search skin directories. +	addSearchSkinDir(mDefaultSkinDir); + +	mSkinDir = getSkinBaseDir(); +	append(mSkinDir, skin_folder); +	// Next level of generality is a skin installed with the viewer. +	addSearchSkinDir(mSkinDir); + +	// user modifications to skins, current and default +	// e.g. c:\documents and settings\users\username\application data\second life\skins\dazzle +	mUserSkinDir = getOSUserAppDir(); +	append(mUserSkinDir, "skins"); +	mUserDefaultSkinDir = mUserSkinDir; +	append(mUserDefaultSkinDir, "default"); +	append(mUserSkinDir, skin_folder); +	// Next level of generality is user modifications to default skin... +	addSearchSkinDir(mUserDefaultSkinDir); +	// then user-defined skins. +	addSearchSkinDir(mUserSkinDir); +} + +void LLDir::addSearchSkinDir(const std::string& skindir) +{ +	if (std::find(mSearchSkinDirs.begin(), mSearchSkinDirs.end(), skindir) == mSearchSkinDirs.end()) +	{ +		LL_DEBUGS("LLDir") << "search skin: '" << skindir << "'" << LL_ENDL; +		mSearchSkinDirs.push_back(skindir); +	} +} + +std::string LLDir::getSkinFolder() const +{ +	return mSkinName; +} + +std::string LLDir::getLanguage() const +{ +	return mLanguage;  }  bool LLDir::setCacheDir(const std::string &path) @@ -664,7 +899,7 @@ bool LLDir::setCacheDir(const std::string &path)  	else  	{  		LLFile::mkdir(path); -		std::string tempname = path + mDirDelimiter + "temp"; +		std::string tempname = add(path, "temp");  		LLFILE* file = LLFile::fopen(tempname,"wt");  		if (file)  		{ @@ -697,6 +932,57 @@ void LLDir::dumpCurrentDirectories()  	LL_DEBUGS2("AppInit","Directories") << "  SkinDir:               " << getSkinDir() << LL_ENDL;  } +std::string LLDir::add(const std::string& path, const std::string& name) const +{ +	std::string destpath(path); +	append(destpath, name); +	return destpath; +} + +void LLDir::append(std::string& destpath, const std::string& name) const +{ +	// Delegate question of whether we need a separator to helper method. +	SepOff sepoff(needSep(destpath, name)); +	if (sepoff.first)               // do we need a separator? +	{ +		destpath += mDirDelimiter; +	} +	// If destpath ends with a separator, AND name starts with one, skip +	// name's leading separator. +	destpath += name.substr(sepoff.second); +} + +LLDir::SepOff LLDir::needSep(const std::string& path, const std::string& name) const +{ +	if (path.empty() || name.empty()) +	{ +		// If either path or name are empty, we do not need a separator +		// between them. +		return SepOff(false, 0); +	} +	// Here we know path and name are both non-empty. But if path already ends +	// with a separator, or if name already starts with a separator, we need +	// not add one. +	std::string::size_type seplen(mDirDelimiter.length()); +	bool path_ends_sep(path.substr(path.length() - seplen) == mDirDelimiter); +	bool name_starts_sep(name.substr(0, seplen) == mDirDelimiter); +	if ((! path_ends_sep) && (! name_starts_sep)) +	{ +		// If neither path nor name brings a separator to the junction, then +		// we need one. +		return SepOff(true, 0); +	} +	if (path_ends_sep && name_starts_sep) +	{ +		// But if BOTH path and name bring a separator, we need not add one. +		// Moreover, we should actually skip the leading separator of 'name'. +		return SepOff(false, seplen); +	} +	// Here we know that either path_ends_sep or name_starts_sep is true -- +	// but not both. So don't add a separator, and don't skip any characters: +	// simple concatenation will do the trick. +	return SepOff(false, 0); +}  void dir_exists_or_crash(const std::string &dir_name)  { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index 3b1883b5d8..c60d3ef3a2 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -56,7 +56,7 @@ typedef enum ELLPath  	LL_PATH_LAST  } ELLPath; - +/// Directory operations  class LLDir  {   public: @@ -100,9 +100,10 @@ class LLDir  	const std::string &getOSCacheDir() const;		// location of OS-specific cache folder (may be empty string)  	const std::string &getCAFile() const;			// File containing TLS certificate authorities  	const std::string &getDirDelimiter() const;	// directory separator for platform (ie. '\' or '/' or ':') +	const std::string &getDefaultSkinDir() const;	// folder for default skin. e.g. c:\program files\second life\skins\default  	const std::string &getSkinDir() const;		// User-specified skin folder. +	const std::string &getUserDefaultSkinDir() const; // dir with user modifications to default skin  	const std::string &getUserSkinDir() const;		// User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin -	const std::string &getDefaultSkinDir() const;	// folder for default skin. e.g. c:\program files\second life\skins\default  	const std::string getSkinBaseDir() const;		// folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins  	const std::string &getLLPluginDir() const;		// Directory containing plugins and plugin shell @@ -117,10 +118,61 @@ class LLDir  	std::string getExtension(const std::string& filepath) const; // Excludes '.', e.g getExtension("foo.wav") == "wav"  	// these methods search the various skin paths for the specified file in the following order: -	// getUserSkinDir(), getSkinDir(), getDefaultSkinDir() -	std::string findSkinnedFilename(const std::string &filename) const; -	std::string findSkinnedFilename(const std::string &subdir, const std::string &filename) const; -	std::string findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const; +	// getUserSkinDir(), getUserDefaultSkinDir(), getSkinDir(), getDefaultSkinDir() +	/// param value for findSkinnedFilenames(), explained below +	enum ESkinConstraint { CURRENT_SKIN, ALL_SKINS }; +	/** +	 * Given a filename within skin, return an ordered sequence of paths to +	 * search. Nonexistent files will be filtered out -- which means that the +	 * vector might be empty. +	 * +	 * @param subdir Identify top-level skin subdirectory by passing one of +	 * LLDir::XUI (file lives under "xui" subtree), LLDir::TEXTURES (file +	 * lives under "textures" subtree), LLDir::SKINBASE (file lives at top +	 * level of skin subdirectory). +	 * @param filename Desired filename within subdir within skin, e.g. +	 * "panel_login.xml". DO NOT prepend (e.g.) "xui" or the desired language. +	 * @param constraint Callers perform two different kinds of processing. +	 * When fetching a XUI file, for instance, the existence of @a filename in +	 * the specified skin completely supercedes any @a filename in the default +	 * skin. For that case, leave the default @a constraint=CURRENT_SKIN. The +	 * returned vector will contain only +	 * ".../<i>current_skin</i>/xui/en/<i>filename</i>", +	 * ".../<i>current_skin</i>/xui/<i>current_language</i>/<i>filename</i>". +	 * But for (e.g.) "strings.xml", we want a given skin to be able to +	 * override only specific entries from the default skin. Any string not +	 * defined in the specified skin will be sought in the default skin. For +	 * that case, pass @a constraint=ALL_SKINS. The returned vector will +	 * contain at least ".../default/xui/en/strings.xml", +	 * ".../default/xui/<i>current_language</i>/strings.xml", +	 * ".../<i>current_skin</i>/xui/en/strings.xml", +	 * ".../<i>current_skin</i>/xui/<i>current_language</i>/strings.xml". +	 */ +	std::vector<std::string> findSkinnedFilenames(const std::string& subdir, +												  const std::string& filename, +												  ESkinConstraint constraint=CURRENT_SKIN) const; +	/// Values for findSkinnedFilenames(subdir) parameter +	static const char *XUI, *TEXTURES, *SKINBASE; +	/** +	 * Return the base-language pathname from findSkinnedFilenames(), or +	 * the empty string if no such file exists. Parameters are identical to +	 * findSkinnedFilenames(). This is shorthand for capturing the vector +	 * returned by findSkinnedFilenames(), checking for empty() and then +	 * returning front(). +	 */ +	std::string findSkinnedFilenameBaseLang(const std::string &subdir, +											const std::string &filename, +											ESkinConstraint constraint=CURRENT_SKIN) const; +	/** +	 * Return the "most localized" pathname from findSkinnedFilenames(), or +	 * the empty string if no such file exists. Parameters are identical to +	 * findSkinnedFilenames(). This is shorthand for capturing the vector +	 * returned by findSkinnedFilenames(), checking for empty() and then +	 * returning back(). +	 */ +	std::string findSkinnedFilename(const std::string &subdir, +									const std::string &filename, +									ESkinConstraint constraint=CURRENT_SKIN) const;  	// random filename in common temporary directory  	std::string getTempFilename() const; @@ -132,15 +184,37 @@ class LLDir  	virtual void setChatLogsDir(const std::string &path);		// Set the chat logs dir to this user's dir  	virtual void setPerAccountChatLogsDir(const std::string &username);		// Set the per user chat log directory.  	virtual void setLindenUserDir(const std::string &username);		// Set the linden user dir to this user's dir -	virtual void setSkinFolder(const std::string &skin_folder); +	virtual void setSkinFolder(const std::string &skin_folder, const std::string& language); +	virtual std::string getSkinFolder() const; +	virtual std::string getLanguage() const;  	virtual bool setCacheDir(const std::string &path);  	virtual void dumpCurrentDirectories(); -	 +  	// Utility routine  	std::string buildSLOSCacheDir() const; +	/// Append specified @a name to @a destpath, separated by getDirDelimiter() +	/// if both are non-empty. +	void append(std::string& destpath, const std::string& name) const; +	/// Append specified @a name to @a path, separated by getDirDelimiter() +	/// if both are non-empty. Return result, leaving @a path unmodified. +	std::string add(const std::string& path, const std::string& name) const; +  protected: +	// Does an add() or append() call need a directory delimiter? +	typedef std::pair<bool, unsigned short> SepOff; +	SepOff needSep(const std::string& path, const std::string& name) const; +	// build mSearchSkinDirs without adding duplicates +	void addSearchSkinDir(const std::string& skindir); + +	// Internal to findSkinnedFilenames() +	template <typename FUNCTION> +	void walkSearchSkinDirs(const std::string& subdir, +							const std::vector<std::string>& subsubdirs, +							const std::string& filename, +							const FUNCTION& function) const; +  	std::string mAppName;               // install directory under progams/ ie "SecondLife"     	std::string mExecutablePathAndName; // full path + Filename of .exe  	std::string mExecutableFilename;    // Filename of .exe @@ -158,10 +232,18 @@ protected:  	std::string mDefaultCacheDir;	// default cache diretory  	std::string mOSCacheDir;		// operating system cache dir  	std::string mDirDelimiter; +	std::string mSkinName;           // caller-specified skin name  	std::string mSkinBaseDir;			// Base for skins paths. -	std::string mSkinDir;			// Location for current skin info.  	std::string mDefaultSkinDir;			// Location for default skin info. +	std::string mSkinDir;			// Location for current skin info. +	std::string mUserDefaultSkinDir;		// Location for default skin info.  	std::string mUserSkinDir;			// Location for user-modified skin info. +	// Skin directories to search, most general to most specific. This order +	// works well for composing fine-grained files, in which an individual item +	// in a specific file overrides the corresponding item in more general +	// files. Of course, for a file-level search, iterate backwards. +	std::vector<std::string> mSearchSkinDirs; +	std::string mLanguage;              // Current viewer language  	std::string mLLPluginDir;			// Location for plugins and plugin shell  }; diff --git a/indra/llvfs/tests/lldir_test.cpp b/indra/llvfs/tests/lldir_test.cpp index ea321c5ae9..323f876c12 100644 --- a/indra/llvfs/tests/lldir_test.cpp +++ b/indra/llvfs/tests/lldir_test.cpp @@ -27,11 +27,167 @@  #include "linden_common.h" +#include "llstring.h" +#include "tests/StringVec.h"  #include "../lldir.h"  #include "../lldiriterator.h"  #include "../test/lltut.h" +#include "stringize.h" +#include <boost/foreach.hpp> +#include <boost/assign/list_of.hpp> + +using boost::assign::list_of; + +// We use ensure_equals(..., vec(list_of(...))) not because it's functionally +// required, but because ensure_equals() knows how to format a StringVec. +// Turns out that when ensure_equals() displays a test failure with just +// list_of("string")("another"), you see 'stringanother' vs. '("string", +// "another")'. +StringVec vec(const StringVec& v) +{ +    return v; +} +// For some tests, use a dummy LLDir that uses memory data instead of touching +// the filesystem +struct LLDir_Dummy: public LLDir +{ +    /*----------------------------- LLDir API ------------------------------*/ +    LLDir_Dummy() +    { +        // Initialize important LLDir data members based on the filesystem +        // data below. +        mDirDelimiter = "/"; +        mExecutableDir = "install"; +        mExecutableFilename = "test"; +        mExecutablePathAndName = add(mExecutableDir, mExecutableFilename); +        mWorkingDir = mExecutableDir; +        mAppRODataDir = "install"; +        mSkinBaseDir = add(mAppRODataDir, "skins"); +        mOSUserDir = "user"; +        mOSUserAppDir = mOSUserDir; +        mLindenUserDir = ""; + +        // Make the dummy filesystem look more or less like what we expect in +        // the real one. +        static const char* preload[] = +        { +            // We group these fixture-data pathnames by basename, rather than +            // sorting by full path as you might expect, because the outcome +            // of each test strongly depends on which skins/languages provide +            // a given basename. +            "install/skins/default/colors.xml", +            "install/skins/steam/colors.xml", +            "user/skins/default/colors.xml", +            "user/skins/steam/colors.xml", + +            "install/skins/default/xui/en/strings.xml", +            "install/skins/default/xui/fr/strings.xml", +            "install/skins/steam/xui/en/strings.xml", +            "install/skins/steam/xui/fr/strings.xml", +            "user/skins/default/xui/en/strings.xml", +            "user/skins/default/xui/fr/strings.xml", +            "user/skins/steam/xui/en/strings.xml", +            "user/skins/steam/xui/fr/strings.xml", + +            "install/skins/default/xui/en/floater.xml", +            "install/skins/default/xui/fr/floater.xml", +            "user/skins/default/xui/fr/floater.xml", + +            "install/skins/default/xui/en/newfile.xml", +            "install/skins/default/xui/fr/newfile.xml", +            "user/skins/default/xui/en/newfile.xml", + +            "install/skins/default/html/en-us/welcome.html", +            "install/skins/default/html/fr/welcome.html", + +            "install/skins/default/textures/only_default.jpeg", +            "install/skins/steam/textures/only_steam.jpeg", +            "user/skins/default/textures/only_user_default.jpeg", +            "user/skins/steam/textures/only_user_steam.jpeg", + +            "install/skins/default/future/somefile.txt" +        }; +        BOOST_FOREACH(const char* path, preload) +        { +            buildFilesystem(path); +        } +    } + +    virtual ~LLDir_Dummy() {} + +    virtual void initAppDirs(const std::string& app_name, const std::string& app_read_only_data_dir) +    { +        // Implement this when we write a test that needs it +    } + +    virtual std::string getCurPath() +    { +        // Implement this when we write a test that needs it +        return ""; +    } + +    virtual U32 countFilesInDir(const std::string& dirname, const std::string& mask) +    { +        // Implement this when we write a test that needs it +        return 0; +    } + +    virtual BOOL fileExists(const std::string& pathname) const +    { +        // Record fileExists() calls so we can check whether caching is +        // working right. Certain LLDir calls should be able to make decisions +        // without calling fileExists() again, having already checked existence. +        mChecked.insert(pathname); +        // For our simple flat set of strings, see whether the identical +        // pathname exists in our set. +        return (mFilesystem.find(pathname) != mFilesystem.end()); +    } + +    virtual std::string getLLPluginLauncher() +    { +        // Implement this when we write a test that needs it +        return ""; +    } + +    virtual std::string getLLPluginFilename(std::string base_name) +    { +        // Implement this when we write a test that needs it +        return ""; +    } + +    /*----------------------------- Dummy data -----------------------------*/ +    void clearFilesystem() { mFilesystem.clear(); } +    void buildFilesystem(const std::string& path) +    { +        // Split the pathname on slashes, ignoring leading, trailing, doubles +        StringVec components; +        LLStringUtil::getTokens(path, components, "/"); +        // Ensure we have an entry representing every level of this path +        std::string partial; +        BOOST_FOREACH(std::string component, components) +        { +            append(partial, component); +            mFilesystem.insert(partial); +        } +    } + +    void clear_checked() { mChecked.clear(); } +    void ensure_checked(const std::string& pathname) const +    { +        tut::ensure(STRINGIZE(pathname << " was not checked but should have been"), +                    mChecked.find(pathname) != mChecked.end()); +    } +    void ensure_not_checked(const std::string& pathname) const +    { +        tut::ensure(STRINGIZE(pathname << " was checked but should not have been"), +                    mChecked.find(pathname) == mChecked.end()); +    } + +    std::set<std::string> mFilesystem; +    mutable std::set<std::string> mChecked; +};  namespace tut  { @@ -419,5 +575,193 @@ namespace tut        LLFile::rmdir(dir1);        LLFile::rmdir(dir2);     } -} +    template<> template<> +    void LLDirTest_object_t::test<6>() +    { +        set_test_name("findSkinnedFilenames()"); +        LLDir_Dummy lldir; +        /*------------------------ "default", "en" -------------------------*/ +        // Setting "default" means we shouldn't consider any "*/skins/steam" +        // directories; setting "en" means we shouldn't consider any "xui/fr" +        // directories. +        lldir.setSkinFolder("default", "en"); +        ensure_equals(lldir.getSkinFolder(), "default"); +        ensure_equals(lldir.getLanguage(), "en"); + +        // top-level directory of a skin isn't localized +        ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), +                      vec(list_of("install/skins/default/colors.xml") +                                 ("user/skins/default/colors.xml"))); +        // We should not have needed to check for skins/default/en. We should +        // just "know" that SKINBASE is not localized. +        lldir.ensure_not_checked("install/skins/default/en"); + +        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), +                      vec(list_of("install/skins/default/textures/only_default.jpeg"))); +        // Nor should we have needed to check skins/default/textures/en +        // because textures is known not to be localized. +        lldir.ensure_not_checked("install/skins/default/textures/en"); + +        StringVec expected(vec(list_of("install/skins/default/xui/en/strings.xml") +                               ("user/skins/default/xui/en/strings.xml"))); +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), +                      expected); +        // The first time, we had to probe to find out whether xui was localized. +        lldir.ensure_checked("install/skins/default/xui/en"); +        lldir.clear_checked(); +        // Now make the same call again -- should return same result -- +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), +                      expected); +        // but this time it should remember that xui is localized. +        lldir.ensure_not_checked("install/skins/default/xui/en"); + +        // localized subdir with "en-us" instead of "en" +        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), +                      vec(list_of("install/skins/default/html/en-us/welcome.html"))); +        lldir.ensure_checked("install/skins/default/html/en"); +        lldir.ensure_checked("install/skins/default/html/en-us"); +        lldir.clear_checked(); +        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), +                      vec(list_of("install/skins/default/html/en-us/welcome.html"))); +        lldir.ensure_not_checked("install/skins/default/html/en"); +        lldir.ensure_not_checked("install/skins/default/html/en-us"); + +        ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), +                      vec(list_of("install/skins/default/future/somefile.txt"))); +        // Test probing for an unrecognized unlocalized future subdir. +        lldir.ensure_checked("install/skins/default/future/en"); +        lldir.clear_checked(); +        ensure_equals(lldir.findSkinnedFilenames("future", "somefile.txt"), +                      vec(list_of("install/skins/default/future/somefile.txt"))); +        // Second time it should remember that future is unlocalized. +        lldir.ensure_not_checked("install/skins/default/future/en"); + +        // When language is set to "en", requesting an html file pulls up the +        // "en-us" version -- not because it magically matches those strings, +        // but because there's no "en" localization and it falls back on the +        // default "en-us"! Note that it would probably still be better to +        // make the default localization be "en" and allow "en-gb" (or +        // whatever) localizations, which would work much more the way you'd +        // expect. +        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), +                      vec(list_of("install/skins/default/html/en-us/welcome.html"))); + +        /*------------------------ "default", "fr" -------------------------*/ +        // We start being able to distinguish localized subdirs from +        // unlocalized when we ask for a non-English language. +        lldir.setSkinFolder("default", "fr"); +        ensure_equals(lldir.getLanguage(), "fr"); + +        // pass merge=true to request this filename in all relevant skins +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), +                      vec(list_of +                          ("install/skins/default/xui/en/strings.xml") +                          ("install/skins/default/xui/fr/strings.xml") +                          ("user/skins/default/xui/en/strings.xml") +                          ("user/skins/default/xui/fr/strings.xml"))); + +        // pass (or default) merge=false to request only most specific skin +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), +                      vec(list_of +                          ("user/skins/default/xui/en/strings.xml") +                          ("user/skins/default/xui/fr/strings.xml"))); + +        // Our dummy floater.xml has a user localization (for "fr") but no +        // English override. This is a case in which CURRENT_SKIN nonetheless +        // returns paths from two different skins. +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"), +                      vec(list_of +                          ("install/skins/default/xui/en/floater.xml") +                          ("user/skins/default/xui/fr/floater.xml"))); + +        // Our dummy newfile.xml has an English override but no user +        // localization. This is another case in which CURRENT_SKIN +        // nonetheless returns paths from two different skins. +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"), +                      vec(list_of +                          ("user/skins/default/xui/en/newfile.xml") +                          ("install/skins/default/xui/fr/newfile.xml"))); + +        ensure_equals(lldir.findSkinnedFilenames("html", "welcome.html"), +                      vec(list_of +                          ("install/skins/default/html/en-us/welcome.html") +                          ("install/skins/default/html/fr/welcome.html"))); + +        /*------------------------ "default", "zh" -------------------------*/ +        lldir.setSkinFolder("default", "zh"); +        // Because strings.xml has only a "fr" override but no "zh" override +        // in any skin, the most localized version we can find is "en". +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), +                      vec(list_of("user/skins/default/xui/en/strings.xml"))); + +        /*------------------------- "steam", "en" --------------------------*/ +        lldir.setSkinFolder("steam", "en"); + +        ensure_equals(lldir.findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", LLDir::ALL_SKINS), +                      vec(list_of +                          ("install/skins/default/colors.xml") +                          ("install/skins/steam/colors.xml") +                          ("user/skins/default/colors.xml") +                          ("user/skins/steam/colors.xml"))); + +        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_default.jpeg"), +                      vec(list_of("install/skins/default/textures/only_default.jpeg"))); + +        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_steam.jpeg"), +                      vec(list_of("install/skins/steam/textures/only_steam.jpeg"))); + +        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_default.jpeg"), +                      vec(list_of("user/skins/default/textures/only_user_default.jpeg"))); + +        ensure_equals(lldir.findSkinnedFilenames(LLDir::TEXTURES, "only_user_steam.jpeg"), +                      vec(list_of("user/skins/steam/textures/only_user_steam.jpeg"))); + +        // CURRENT_SKIN +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), +                      vec(list_of("user/skins/steam/xui/en/strings.xml"))); + +        // pass constraint=ALL_SKINS to request this filename in all relevant skins +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), +                      vec(list_of +                          ("install/skins/default/xui/en/strings.xml") +                          ("install/skins/steam/xui/en/strings.xml") +                          ("user/skins/default/xui/en/strings.xml") +                          ("user/skins/steam/xui/en/strings.xml"))); + +        /*------------------------- "steam", "fr" --------------------------*/ +        lldir.setSkinFolder("steam", "fr"); + +        // pass CURRENT_SKIN to request only the most specialized files +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"), +                      vec(list_of +                          ("user/skins/steam/xui/en/strings.xml") +                          ("user/skins/steam/xui/fr/strings.xml"))); + +        // pass ALL_SKINS to request this filename in all relevant skins +        ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", LLDir::ALL_SKINS), +                      vec(list_of +                          ("install/skins/default/xui/en/strings.xml") +                          ("install/skins/default/xui/fr/strings.xml") +                          ("install/skins/steam/xui/en/strings.xml") +                          ("install/skins/steam/xui/fr/strings.xml") +                          ("user/skins/default/xui/en/strings.xml") +                          ("user/skins/default/xui/fr/strings.xml") +                          ("user/skins/steam/xui/en/strings.xml") +                          ("user/skins/steam/xui/fr/strings.xml"))); +    } + +    template<> template<> +    void LLDirTest_object_t::test<7>() +    { +        set_test_name("add()"); +        LLDir_Dummy lldir; +        ensure_equals("both empty", lldir.add("", ""), ""); +        ensure_equals("path empty", lldir.add("", "b"), "b"); +        ensure_equals("name empty", lldir.add("a", ""), "a"); +        ensure_equals("both simple", lldir.add("a", "b"), "a/b"); +        ensure_equals("name leading slash", lldir.add("a", "/b"), "a/b"); +        ensure_equals("path trailing slash", lldir.add("a/", "b"), "a/b"); +        ensure_equals("both bring slashes", lldir.add("a/", "/b"), "a/b"); +    } +} diff --git a/indra/llxml/llxmlnode.cpp b/indra/llxml/llxmlnode.cpp index 2ffb0d8503..b775249219 100644 --- a/indra/llxml/llxmlnode.cpp +++ b/indra/llxml/llxmlnode.cpp @@ -897,7 +897,8 @@ bool LLXMLNode::getLayeredXMLNode(LLXMLNodePtr& root,  	std::vector<std::string>::const_iterator itor; -	for (itor = paths.begin(), ++itor; itor != paths.end(); ++itor) +	// We've already dealt with the first item, skip that one +	for (itor = paths.begin() + 1; itor != paths.end(); ++itor)  	{  		std::string layer_filename = *itor;  		if(layer_filename.empty() || layer_filename == filename) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index f2684bc96a..4fa1663907 100755 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -4051,17 +4051,6 @@        <key>Value</key>        <integer>305</integer>      </map> -    <key>HelpUseLocal</key> -    <map> -      <key>Comment</key> -      <string>If set, always use this for help: skins/default/html/[LANGUAGE]/help-offline/index.html</string> -      <key>Persist</key> -      <integer>0</integer> -      <key>Type</key> -      <string>Boolean</string> -      <key>Value</key> -      <integer>0</integer> -    </map>      <key>HelpURLFormat</key>      <map>        <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index a674ab0cf8..fddf8bd7a3 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -124,7 +124,6 @@  #include <boost/algorithm/string.hpp> -  #if LL_WINDOWS  #	include <share.h> // For _SH_DENYWR in initMarkerFile  #else @@ -693,7 +692,7 @@ bool LLAppViewer::init()  	gDirUtilp->initAppDirs("SecondLife");  	// set skin search path to default, will be overridden later  	// this allows simple skinned file lookups to work -	gDirUtilp->setSkinFolder("default"); +	gDirUtilp->setSkinFolder("default", "en");  	initLogging(); @@ -781,10 +780,15 @@ bool LLAppViewer::init()  		&LLUI::sGLScaleFactor);  	LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ; -	// Setup paths and LLTrans after LLUI::initClass has been called. +	// NOW LLUI::getLanguage() should work. gDirUtilp must know the language +	// for this session ASAP so all the file-loading commands that follow, +	// that use findSkinnedFilenames(), will include the localized files. +	gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), LLUI::getLanguage()); + +	// Setup LLTrans after LLUI::initClass has been called.  	initStrings(); -	// Setup notifications after LLUI::setupPaths() has been called. +	// Setup notifications after LLUI::initClass() has been called.  	LLNotifications::instance();  	LL_INFOS("InitInfo") << "Notifications initialized." << LL_ENDL ; @@ -2575,13 +2579,15 @@ bool LLAppViewer::initConfiguration()  		LLStartUp::setStartSLURL(start_slurl);      } -    const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); -    if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) -    {    -		// hack to force the skin to default. -        gDirUtilp->setSkinFolder(skinfolder->getValue().asString()); -		//gDirUtilp->setSkinFolder("default"); -    } +	const LLControlVariable* skinfolder = gSavedSettings.getControl("SkinCurrent"); +	if(skinfolder && LLStringUtil::null != skinfolder->getValue().asString()) +	{	 +		// Examining "Language" may not suffice -- see LLUI::getLanguage() +		// logic. Unfortunately LLUI::getLanguage() doesn't yet do us much +		// good because we haven't yet called LLUI::initClass(). +		gDirUtilp->setSkinFolder(skinfolder->getValue().asString(), +								 gSavedSettings.getString("Language")); +	}  	if (gSavedSettings.getBOOL("SpellCheck"))  	{ @@ -3652,8 +3658,7 @@ void LLAppViewer::migrateCacheDirectory()  	{  		gSavedSettings.setBOOL("MigrateCacheDirectory", FALSE); -		std::string delimiter = gDirUtilp->getDirDelimiter(); -		std::string old_cache_dir = gDirUtilp->getOSUserAppDir() + delimiter + "cache"; +		std::string old_cache_dir = gDirUtilp->add(gDirUtilp->getOSUserAppDir(), "cache");  		std::string new_cache_dir = gDirUtilp->getCacheDir(true);  		if (gDirUtilp->fileExists(old_cache_dir)) @@ -3669,8 +3674,8 @@ void LLAppViewer::migrateCacheDirectory()  			while (iter.next(file_name))  			{  				if (file_name == "." || file_name == "..") continue; -				std::string source_path = old_cache_dir + delimiter + file_name; -				std::string dest_path = new_cache_dir + delimiter + file_name; +				std::string source_path = gDirUtilp->add(old_cache_dir, file_name); +				std::string dest_path = gDirUtilp->add(new_cache_dir, file_name);  				if (!LLFile::rename(source_path, dest_path))  				{  					file_count++; @@ -3901,7 +3906,7 @@ bool LLAppViewer::initCache()  		LLDirIterator iter(dir, mask);  		if (iter.next(found_file))  		{ -			old_vfs_data_file = dir + gDirUtilp->getDirDelimiter() + found_file; +			old_vfs_data_file = gDirUtilp->add(dir, found_file);  			S32 start_pos = found_file.find_last_of('.');  			if (start_pos > 0) @@ -5216,20 +5221,20 @@ void LLAppViewer::launchUpdater()  	// we tell the updater where to find the xml containing string  	// translations which it can use for its own UI  	std::string xml_strings_file = "strings.xml"; -	std::vector<std::string> xui_path_vec = LLUI::getXUIPaths(); +	std::vector<std::string> xui_path_vec = +		gDirUtilp->findSkinnedFilenames(LLDir::XUI, xml_strings_file);  	std::string xml_search_paths; -	std::vector<std::string>::const_iterator iter; +	const char* delim = "";  	// build comma-delimited list of xml paths to pass to updater -	for (iter = xui_path_vec.begin(); iter != xui_path_vec.end(); ) -	{ -		std::string this_skin_dir = gDirUtilp->getDefaultSkinDir() -			+ gDirUtilp->getDirDelimiter() -			+ (*iter); -		llinfos << "Got a XUI path: " << this_skin_dir << llendl; -		xml_search_paths.append(this_skin_dir); -		++iter; -		if (iter != xui_path_vec.end()) -			xml_search_paths.append(","); // comma-delimit +	BOOST_FOREACH(std::string this_skin_path, xui_path_vec) +	{ +		// Although we already have the full set of paths with the filename +		// appended, the linux-updater.bin command-line switches require us to +		// snip the filename OFF and pass it as a separate switch argument. :-P +		llinfos << "Got a XUI path: " << this_skin_path << llendl; +		xml_search_paths.append(delim); +		xml_search_paths.append(gDirUtilp->getDirName(this_skin_path)); +		delim = ",";  	}  	// build the overall command-line to run the updater correctly  	LLAppViewer::sUpdaterInfo->mUpdateExePath =  diff --git a/indra/newview/lldaycyclemanager.cpp b/indra/newview/lldaycyclemanager.cpp index 347a467a8b..8af2f4ea33 100644 --- a/indra/newview/lldaycyclemanager.cpp +++ b/indra/newview/lldaycyclemanager.cpp @@ -184,7 +184,7 @@ void LLDayCycleManager::loadPresets(const std::string& dir)  	{  		std::string file;  		if (!dir_iter.next(file)) break; // no more files -		loadPreset(dir + file); +		loadPreset(gDirUtilp->add(dir, file));  	}  } diff --git a/indra/newview/llfloaterhelpbrowser.cpp b/indra/newview/llfloaterhelpbrowser.cpp index fd9c37ae73..4cb632bd6a 100644 --- a/indra/newview/llfloaterhelpbrowser.cpp +++ b/indra/newview/llfloaterhelpbrowser.cpp @@ -77,15 +77,7 @@ void LLFloaterHelpBrowser::onOpen(const LLSD& key)  	gSavedSettings.setBOOL("HelpFloaterOpen", TRUE);  	std::string topic = key.asString(); - -	if (topic == "__local") -	{ -		mBrowser->navigateToLocalPage( "help-offline" , "index.html" ); -	} -	else -	{ -		mBrowser->navigateTo(LLViewerHelp::instance().getURL(topic)); -	} +	mBrowser->navigateTo(LLViewerHelp::instance().getURL(topic));  }  //virtual diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index d741b5b133..15e0b89f6c 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -137,7 +137,7 @@ public:  	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, bool save = false);			// needs to be public so live file can call it when it finds an update +	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); @@ -291,7 +291,8 @@ LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater  {  	mSavedLocalization = LLUI::sSettingGroups["config"]->getString("Language");				// save current localization setting  	LLUI::sSettingGroups["config"]->setString("Language", floater->getLocStr(ID));// hack language to be the one we want to preview floaters in -	LLUI::setupPaths();														// forcibly reset XUI paths with this new language +	// forcibly reset XUI paths with this new language +	gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), floater->getLocStr(ID));  }  // Actually reset in destructor @@ -299,7 +300,8 @@ LLLocalizationResetForcer::LLLocalizationResetForcer(LLFloaterUIPreview* floater  LLLocalizationResetForcer::~LLLocalizationResetForcer()  {  	LLUI::sSettingGroups["config"]->setString("Language", mSavedLocalization);	// reset language to what it was before we changed it -	LLUI::setupPaths();														// forcibly reset XUI paths with this new language +	// forcibly reset XUI paths with this new language +	gDirUtilp->setSkinFolder(gDirUtilp->getSkinFolder(), mSavedLocalization);  }  // Live file constructor @@ -488,7 +490,7 @@ BOOL LLFloaterUIPreview::postBuild()  	{  		if((found = iter.next(language_directory)))							// get next directory  		{ -			std::string full_path = xui_dir + language_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; @@ -773,7 +775,8 @@ void LLFloaterUIPreview::onClickDisplayFloater(S32 caller_id)  // Saves the current floater/panel  void LLFloaterUIPreview::onClickSaveFloater(S32 caller_id)  { -	displayFloater(TRUE, caller_id, true); +	displayFloater(TRUE, caller_id); +	popupAndPrintWarning("Save-floater functionality removed, use XML schema to clean up XUI files");  }  // Saves all floater/panels @@ -784,25 +787,15 @@ void LLFloaterUIPreview::onClickSaveAll(S32 caller_id)  	for (int index = 0; index < listSize; index++)  	{  		mFileList->selectNthItem(index); -		displayFloater(TRUE, caller_id, true); +		displayFloater(TRUE, caller_id);  	} -} - -// Given path to floater or panel XML file "filename.xml", -// returns "filename_new.xml" -static std::string append_new_to_xml_filename(const std::string& path) -{ -	std::string full_filename = gDirUtilp->findSkinnedFilename(LLUI::getLocalizedSkinPath(), path); -	std::string::size_type extension_pos = full_filename.rfind(".xml"); -	full_filename.resize(extension_pos); -	full_filename += "_new.xml"; -	return full_filename; +	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, bool save) +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) @@ -843,48 +836,13 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save)  	if(!strncmp(path.c_str(),"floater_",8)  		|| !strncmp(path.c_str(), "inspect_", 8))		// if it's a floater  	{ -		if (save) -		{ -			LLXMLNodePtr floater_write = new LLXMLNode();			 -			(*floaterp)->buildFromFile(path, floater_write);	// just build it - -			if (!floater_write->isNull()) -			{ -				std::string full_filename = append_new_to_xml_filename(path); -				LLFILE* floater_temp = LLFile::fopen(full_filename.c_str(), "w"); -				LLXMLNode::writeHeaderToFile(floater_temp); -				const bool use_type_decorations = false; -				floater_write->writeToFile(floater_temp, std::string(), use_type_decorations); -				fclose(floater_temp); -			} -		} -		else -		{ -			(*floaterp)->buildFromFile(path);	// just build it -			(*floaterp)->openFloater((*floaterp)->getKey()); -			(*floaterp)->setCanResize((*floaterp)->isResizable()); -		} - +		(*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  	{ -		if (save) -		{	 -			LLXMLNodePtr menu_write = new LLXMLNode();	 -			LLMenuGL* menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>(path, gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance(), menu_write); - -			if (!menu_write->isNull()) -			{ -				std::string full_filename = append_new_to_xml_filename(path); -				LLFILE* menu_temp = LLFile::fopen(full_filename.c_str(), "w"); -				LLXMLNode::writeHeaderToFile(menu_temp); -				const bool use_type_decorations = false; -				menu_write->writeToFile(menu_temp, std::string(), use_type_decorations); -				fclose(menu_temp); -			} - -			delete menu; -		} +		// former 'save' processing excised  	}  	else																// if it is a panel...  	{ @@ -896,39 +854,21 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save)  		LLPanel::Params panel_params;  		LLPanel* panel = LLUICtrlFactory::create<LLPanel>(panel_params);	// create a new panel -		if (save) -		{ -			LLXMLNodePtr panel_write = new LLXMLNode(); -			panel->buildFromFile(path, panel_write);		// build it -			 -			if (!panel_write->isNull()) -			{ -				std::string full_filename = append_new_to_xml_filename(path); -				LLFILE* panel_temp = LLFile::fopen(full_filename.c_str(), "w"); -				LLXMLNode::writeHeaderToFile(panel_temp); -				const bool use_type_decorations = false; -				panel_write->writeToFile(panel_temp, std::string(), use_type_decorations); -				fclose(panel_temp); -			} -		} -		else -		{ -			panel->buildFromFile(path);										// build it -			LLRect new_size = panel->getRect();								// get its rectangle -			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?) -		} +		panel->buildFromFile(path);										// build it +		LLRect new_size = panel->getRect();								// get its rectangle +		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) @@ -964,7 +904,7 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save)  	(*floaterp)->center();  	addDependentFloater(*floaterp); -	if(click && ID == 1 && !save) +	if(click && ID == 1)  	{  		// set up live file to track it  		if(mLiveFile) diff --git a/indra/newview/llhints.cpp b/indra/newview/llhints.cpp index e15862e2a4..197408b40e 100644 --- a/indra/newview/llhints.cpp +++ b/indra/newview/llhints.cpp @@ -171,12 +171,12 @@ LLHintPopup::LLHintPopup(const LLHintPopup::Params& p)  	}  	if (p.hint_image.isProvided())  	{ -		buildFromFile("panel_hint_image.xml", NULL, p); +		buildFromFile("panel_hint_image.xml", p);  		getChild<LLIconCtrl>("hint_image")->setImage(p.hint_image());  	}  	else  	{ -		buildFromFile( "panel_hint.xml", NULL, p); +		buildFromFile( "panel_hint.xml", p);  	}  } diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp index 7650fe9229..99b4707158 100644 --- a/indra/newview/llmediactrl.cpp +++ b/indra/newview/llmediactrl.cpp @@ -564,32 +564,13 @@ void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type)  //  void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in )  { -	std::string language = LLUI::getLanguage(); -	std::string delim = gDirUtilp->getDirDelimiter(); -	std::string filename; +	std::string filename(gDirUtilp->add(subdir, filename_in)); +	std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", filename); -	filename += subdir; -	filename += delim; -	filename += filename_in; - -	std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename); - -	if (! gDirUtilp->fileExists(expanded_filename)) +	if (expanded_filename.empty())  	{ -		if (language != "en") -		{ -			expanded_filename = gDirUtilp->findSkinnedFilename("html", "en", filename); -			if (! gDirUtilp->fileExists(expanded_filename)) -			{ -				llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; -				return; -			} -		} -		else -		{ -			llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; -			return; -		} +		llwarns << "File " << filename << "not found" << llendl; +		return;  	}  	if (ensureMediaSourceExists())  	{ @@ -597,7 +578,6 @@ void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::str  		mMediaSource->setSize(mTextureWidth, mTextureHeight);  		mMediaSource->navigateTo(expanded_filename, "text/html", false);  	} -  }  //////////////////////////////////////////////////////////////////////////////// diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index 88727bf59b..9c25e69db0 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -815,7 +815,7 @@ void LLScriptEdCore::onBtnDynamicHelp()  	if (!live_help_floater)  	{  		live_help_floater = new LLFloater(LLSD()); -		live_help_floater->buildFromFile("floater_lsl_guide.xml", NULL); +		live_help_floater->buildFromFile("floater_lsl_guide.xml");  		LLFloater* parent = dynamic_cast<LLFloater*>(getParent());  		llassert(parent);  		if (parent) diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 42648b82c2..3d1fd74ba6 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -3307,11 +3307,8 @@ bool process_login_success_response()  	{  		// replace the default help URL format  		gSavedSettings.setString("HelpURLFormat",text); -		 -		// don't fall back to Standalone's pre-connection static help -		gSavedSettings.setBOOL("HelpUseLocal", false);  	} -			 +  	std::string home_location = response["home"];  	if(!home_location.empty())  	{ diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index 0cb6c85012..2002647fef 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -242,7 +242,7 @@ LLIMWellWindow::RowPanel::RowPanel(const LLSysWellWindow* parent, const LLUUID&  		S32 chicletCounter, const std::string& name, const LLUUID& otherParticipantId) :  		LLPanel(LLPanel::Params()), mChiclet(NULL), mParent(parent)  { -	buildFromFile( "panel_activeim_row.xml", NULL); +	buildFromFile( "panel_activeim_row.xml");  	// Choose which of the pre-created chiclets (IM/group) to use.  	// The other one gets hidden. @@ -356,7 +356,7 @@ LLIMWellWindow::ObjectRowPanel::ObjectRowPanel(const LLUUID& notification_id, bo   : LLPanel()   , mChiclet(NULL)  { -	buildFromFile( "panel_active_object_row.xml", NULL); +	buildFromFile( "panel_active_object_row.xml");  	initChiclet(notification_id); diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index 0eec7f0afd..9dfb29b905 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -118,7 +118,7 @@ LLToast::LLToast(const LLToast::Params& p)  {  	mTimer.reset(new LLToastLifeTimer(this, p.lifetime_secs)); -	buildFromFile("panel_toast.xml", NULL); +	buildFromFile("panel_toast.xml");  	setCanDrag(FALSE); diff --git a/indra/newview/llviewerhelp.cpp b/indra/newview/llviewerhelp.cpp index a8a918f259..04c2e27c9d 100644 --- a/indra/newview/llviewerhelp.cpp +++ b/indra/newview/llviewerhelp.cpp @@ -71,12 +71,6 @@ LLHelpHandler gHelpHandler;  std::string LLViewerHelp::getURL(const std::string &topic)  { -	// allow overriding the help server with a local help file -	if( gSavedSettings.getBOOL("HelpUseLocal") ) -	{ -		return "__local"; -	} -  	// if the help topic is empty, use the default topic  	std::string help_topic = topic;  	if (help_topic.empty()) diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 1eb4bedfaf..47059b0b8c 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -1184,12 +1184,9 @@ void LLViewerMedia::clearAllCookies()  	LLDirIterator dir_iter(base_dir, "*_*");  	while (dir_iter.next(filename))  	{ -		target = base_dir; -		target += filename; -		target += gDirUtilp->getDirDelimiter(); -		target += "browser_profile"; -		target += gDirUtilp->getDirDelimiter(); -		target += "cookies"; +		target = gDirUtilp->add(base_dir, filename); +		gDirUtilp->append(target, "browser_profile"); +		gDirUtilp->append(target, "cookies");  		lldebugs << "target = " << target << llendl;  		if(LLFile::isfile(target))  		{	 @@ -1197,10 +1194,8 @@ void LLViewerMedia::clearAllCookies()  		}  		// Other accounts may have new-style cookie files too -- delete them as well -		target = base_dir; -		target += filename; -		target += gDirUtilp->getDirDelimiter(); -		target += PLUGIN_COOKIE_FILE_NAME; +		target = gDirUtilp->add(base_dir, filename); +		gDirUtilp->append(target, PLUGIN_COOKIE_FILE_NAME);  		lldebugs << "target = " << target << llendl;  		if(LLFile::isfile(target))  		{	 diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index e4669cde34..49d7e8b842 100644 --- a/indra/newview/llviewertexturelist.cpp +++ b/indra/newview/llviewertexturelist.cpp @@ -1686,49 +1686,43 @@ struct UIImageDeclarations : public LLInitParam::Block<UIImageDeclarations>  bool LLUIImageList::initFromFile()  { -	// construct path to canonical textures.xml in default skin dir -	std::string base_file_path = gDirUtilp->getExpandedFilename(LL_PATH_SKINS, "default", "textures", "textures.xml"); +	// Look for textures.xml in all the right places. Pass +	// constraint=LLDir::ALL_SKINS because we want to overlay textures.xml +	// from all the skins directories. +	std::vector<std::string> textures_paths = +		gDirUtilp->findSkinnedFilenames(LLDir::TEXTURES, "textures.xml", LLDir::ALL_SKINS); +	std::vector<std::string>::const_iterator pi(textures_paths.begin()), pend(textures_paths.end()); +	if (pi == pend) +	{ +		llwarns << "No textures.xml found in skins directories" << llendl; +		return false; +	} +	// The first (most generic) file gets special validations  	LLXMLNodePtr root; - -	if (!LLXMLNode::parseFile(base_file_path, root, NULL)) +	if (!LLXMLNode::parseFile(*pi, root, NULL))  	{ -		llwarns << "Unable to parse UI image list file " << base_file_path << llendl; +		llwarns << "Unable to parse UI image list file " << *pi << llendl;  		return false;  	}  	if (!root->hasAttribute("version"))  	{ -		llwarns << "No valid version number in UI image list file " << base_file_path << llendl; +		llwarns << "No valid version number in UI image list file " << *pi << llendl;  		return false;  	}  	UIImageDeclarations images;  	LLXUIParser parser; -	parser.readXUI(root, images, base_file_path); - -	// add components defined in current skin -	std::string skin_update_path = gDirUtilp->getSkinDir()  -									+ gDirUtilp->getDirDelimiter()  -									+ "textures" -									+ gDirUtilp->getDirDelimiter() -									+ "textures.xml"; -	LLXMLNodePtr update_root; -	if (skin_update_path != base_file_path -		&& LLXMLNode::parseFile(skin_update_path, update_root, NULL)) -	{ -		parser.readXUI(update_root, images, skin_update_path); -	} - -	// add components defined in user override of current skin -	skin_update_path = gDirUtilp->getUserSkinDir()  -						+ gDirUtilp->getDirDelimiter()  -						+ "textures" -						+ gDirUtilp->getDirDelimiter() -						+ "textures.xml"; -	if (skin_update_path != base_file_path -		&& LLXMLNode::parseFile(skin_update_path, update_root, NULL)) -	{ -		parser.readXUI(update_root, images, skin_update_path); +	parser.readXUI(root, images, *pi); + +	// add components defined in the rest of the skin paths +	while (++pi != pend) +	{ +		LLXMLNodePtr update_root; +		if (LLXMLNode::parseFile(*pi, update_root, NULL)) +		{ +			parser.readXUI(update_root, images, *pi); +		}  	}  	if (!images.validateBlock()) return false; diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index c3af0f6a51..44e6d70546 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -1692,8 +1692,7 @@ LLViewerWindow::LLViewerWindow(const Params& p)  	LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"),  								mDisplayScale.mV[VX],  								mDisplayScale.mV[VY], -								gDirUtilp->getAppRODataDir(), -								LLUI::getXUIPaths()); +								gDirUtilp->getAppRODataDir());  	// Create container for all sub-views  	LLView::Params rvp; @@ -4764,8 +4763,7 @@ void LLViewerWindow::initFonts(F32 zoom_factor)  	LLFontGL::initClass( gSavedSettings.getF32("FontScreenDPI"),  								mDisplayScale.mV[VX] * zoom_factor,  								mDisplayScale.mV[VY] * zoom_factor, -								gDirUtilp->getAppRODataDir(), -								LLUI::getXUIPaths()); +								gDirUtilp->getAppRODataDir());  	// Force font reloads, which can be very slow  	LLFontGL::loadDefaultFonts();  } diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index e386112334..4f52ff9778 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -100,7 +100,7 @@ void LLWaterParamManager::loadPresetsFromDir(const std::string& dir)  			break; // no more files  		} -		std::string path = dir + file; +		std::string path = gDirUtilp->add(dir, file);  		if (!loadPreset(path))  		{  			llwarns << "Error loading water preset from " << path << llendl; diff --git a/indra/newview/llwlparammanager.cpp b/indra/newview/llwlparammanager.cpp index 49d9d44d74..6077208799 100644 --- a/indra/newview/llwlparammanager.cpp +++ b/indra/newview/llwlparammanager.cpp @@ -283,7 +283,7 @@ void LLWLParamManager::loadPresetsFromDir(const std::string& dir)  			break; // no more files  		} -		std::string path = dir + file; +		std::string path = gDirUtilp->add(dir, file);  		if (!loadPreset(path))  		{  			llwarns << "Error loading sky preset from " << path << llendl; diff --git a/indra/newview/skins/paths.xml b/indra/newview/skins/paths.xml deleted file mode 100644 index 3c0da041c7..0000000000 --- a/indra/newview/skins/paths.xml +++ /dev/null @@ -1,10 +0,0 @@ -<paths>  -	<directory> -    <subdir>xui</subdir> -    <subdir>en</subdir> -  </directory> -	<directory> -    <subdir>xui</subdir> -    <subdir>[LANGUAGE]</subdir> -  </directory> -</paths>
\ No newline at end of file diff --git a/indra/newview/tests/lldir_stub.cpp b/indra/newview/tests/lldir_stub.cpp index 18cf4e7419..3c0a4377d8 100644 --- a/indra/newview/tests/lldir_stub.cpp +++ b/indra/newview/tests/lldir_stub.cpp @@ -32,7 +32,7 @@ BOOL LLDir::deleteFilesInDir(const std::string &dirname, const std::string &mask  void LLDir::setChatLogsDir(const std::string &path) {}  void LLDir::setPerAccountChatLogsDir(const std::string &first, const std::string &last) {}  void LLDir::setLindenUserDir(const std::string &first, const std::string &last) {} -void LLDir::setSkinFolder(const std::string &skin_folder) {} +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language) {}  bool LLDir::setCacheDir(const std::string &path) { return true; }  void LLDir::dumpCurrentDirectories() {} diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index 8338a27140..86f978faf1 100644 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -115,7 +115,6 @@ class ViewerManifest(LLManifest):              # skins              if self.prefix(src="skins"): -                    self.path("paths.xml")                      # include the entire textures directory recursively                      if self.prefix(src="*/textures"):                              self.path("*/*.tga") @@ -133,11 +132,18 @@ class ViewerManifest(LLManifest):                      self.path("*/*.xml")                      # Local HTML files (e.g. loading screen) -                    if self.prefix(src="*/html"): +                    # The claim is that we never use local html files any +                    # longer. But rather than commenting out this block, let's +                    # rename every html subdirectory as html.old. That way, if +                    # we're wrong, a user actually does have the relevant +                    # files; s/he just needs to rename every html.old +                    # directory back to html to recover them. +                    if self.prefix(src="*/html", dst="*/html.old"):                              self.path("*.png")                              self.path("*/*/*.html")                              self.path("*/*/*.gif")                              self.end_prefix("*/html") +                      self.end_prefix("skins")              # local_assets dir (for pre-cached textures) diff --git a/indra/test/lluuidhashmap_tut.cpp b/indra/test/lluuidhashmap_tut.cpp index 0544e832ce..408bc3faf1 100644 --- a/indra/test/lluuidhashmap_tut.cpp +++ b/indra/test/lluuidhashmap_tut.cpp @@ -30,6 +30,10 @@  #include "linden_common.h"  #include "lluuidhashmap.h"  #include "llsdserialize.h" +#include "lldir.h" +#include "stringize.h" +#include <iostream> +#include <fstream>  namespace tut  { @@ -79,40 +83,133 @@ namespace tut  	template<> template<>  	void hash_index_object_t::test<1>()  	{ -		LLUUIDHashMap<UUIDTableEntry, 32>	hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry()); +		set_test_name("stress test"); +		// As of 2012-10-10, I (nat) have observed sporadic failures of this +		// test: "set/get did not work." The trouble is that since test data +		// are randomly generated with every run, it is impossible to debug a +		// test failure. One is left with the uneasy suspicion that +		// LLUUID::generate() can sometimes produce duplicates even within the +		// moderately small number requested here. Since rerunning the test +		// generally allows it to pass, it's too easy to shrug and forget it. +		// The following code is intended to support reproducing such test +		// failures. The idea is that, on test failure, we save the generated +		// data to a canonical filename in a temp directory. Then on every +		// subsequent run, we check for that filename. If it exists, we reload +		// that specific data rather than generating fresh data -- which +		// should presumably reproduce the same test failure. But we inform +		// the user that to resume normal (random) test runs, s/he need only +		// delete that file. And since it's in a temp directory, sooner or +		// later the system will clean it up anyway. +		const char* tempvar = "TEMP"; +		const char* tempdir = getenv(tempvar); // Windows convention +		if (! tempdir) +		{ +			tempvar = "TMPDIR"; +			tempdir = getenv(tempvar); // Mac convention +		} +		if (! tempdir) +		{ +			// reset tempvar to the first var we check; it's just a +			// recommendation +			tempvar = "TEMP"; +			tempdir = "/tmp";		// Posix in general +		} +		std::string savefile(gDirUtilp->add(tempdir, "lluuidhashmap_tut.save.txt"));  		const int numElementsToCheck = 32*256*32; -		std::vector<LLUUID> idList(numElementsToCheck); -		int i; -		 -		for (i = 0; i < numElementsToCheck; i++) +		std::vector<LLUUID> idList; +		if (gDirUtilp->fileExists(savefile))  		{ -			LLUUID id; -			id.generate(); -			UUIDTableEntry entry(id, i); -			hashTable.set(id, entry); -			idList[i] = id; +			// We have saved data from a previous failed run. Reload that data. +			std::ifstream inf(savefile.c_str()); +			if (! inf.is_open()) +			{ +				fail(STRINGIZE("Although save file '" << savefile << "' exists, it cannot be opened")); +			} +			std::string item; +			while (std::getline(inf, item)) +			{ +				idList.push_back(LLUUID(item)); +			} +			std::cout << "Reloaded " << idList.size() << " items from '" << savefile << "'"; +			if (idList.size() != numElementsToCheck) +			{ +				std::cout << " (expected " << numElementsToCheck << ")"; +			} +			std::cout << " -- delete this file to generate new data" << std::endl; +		} +		else +		{ +			// savefile does not exist (normal case): regenerate idList from +			// scratch. +			for (int i = 0; i < numElementsToCheck; ++i) +			{ +				LLUUID id; +				id.generate(); +				idList.push_back(id); +			}  		} -		for (i = 0; i < numElementsToCheck; i++) +		LLUUIDHashMap<UUIDTableEntry, 32>	hashTable(UUIDTableEntry::uuidEq, UUIDTableEntry()); +		int i; +		 +		for (i = 0; i < idList.size(); ++i)  		{ -			LLUUID idToCheck = idList[i]; -			UUIDTableEntry entryToCheck = hashTable.get(idToCheck); -			ensure("set/get did not work", entryToCheck.getID() == idToCheck && entryToCheck.getValue() == (size_t)i); +			UUIDTableEntry entry(idList[i], i); +			hashTable.set(idList[i], entry);  		} -		for (i = 0; i < numElementsToCheck; i++) +		try  		{ -			LLUUID idToCheck = idList[i]; -			if (i % 2 != 0) +			for (i = 0; i < idList.size(); i++)  			{ -				hashTable.remove(idToCheck); +				LLUUID idToCheck = idList[i]; +				UUIDTableEntry entryToCheck = hashTable.get(idToCheck); +				ensure_equals(STRINGIZE("set/get ID (entry " << i << ")").c_str(), +							  entryToCheck.getID(), idToCheck); +				ensure_equals(STRINGIZE("set/get value (ID " << idToCheck << ")").c_str(), +							  entryToCheck.getValue(), (size_t)i);  			} -		} -		for (i = 0; i < numElementsToCheck; i++) +			for (i = 0; i < idList.size(); i++) +			{ +				LLUUID idToCheck = idList[i]; +				if (i % 2 != 0) +				{ +					hashTable.remove(idToCheck); +				} +			} + +			for (i = 0; i < idList.size(); i++) +			{ +				LLUUID idToCheck = idList[i]; +				ensure("remove or check did not work", (i % 2 == 0 && hashTable.check(idToCheck)) || (i % 2 != 0 && !hashTable.check(idToCheck))); +			} +		} +		catch (const failure&)  		{ -			LLUUID idToCheck = idList[i]; -			ensure("remove or check did not work", (i % 2 == 0 && hashTable.check(idToCheck)) || (i % 2 != 0 && !hashTable.check(idToCheck))); +			// One of the above tests failed. Try to save idList to repro with +			// a later run. +			std::ofstream outf(savefile.c_str()); +			if (! outf.is_open()) +			{ +				// Sigh, don't use fail() here because we want to preserve +				// the original test failure. +				std::cout << "Cannot open file '" << savefile +						  << "' to save data -- check and fix " << tempvar << std::endl; +			} +			else +			{ +				// outf.is_open() +				for (int i = 0; i < idList.size(); ++i) +				{ +					outf << idList[i] << std::endl; +				} +				std::cout << "Saved " << idList.size() << " entries to '" << savefile +						  << "' -- rerun test to debug with these" << std::endl; +			} +			// re-raise the same exception -- we WANT this test failure to +			// be reported! We just needed to save the data on the way out. +			throw;  		}  	} diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp index 7c016fecf9..db52e6c55f 100644 --- a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -78,7 +78,9 @@ S32 LLDir::deleteFilesInDir(const std::string &dirname,  void LLDir::setChatLogsDir(const std::string &path){}		  void LLDir::setPerAccountChatLogsDir(const std::string &username){}  void LLDir::setLindenUserDir(const std::string &username){}		 -void LLDir::setSkinFolder(const std::string &skin_folder){} +void LLDir::setSkinFolder(const std::string &skin_folder, const std::string& language){} +std::string LLDir::getSkinFolder() const { return "default"; } +std::string LLDir::getLanguage() const { return "en"; }  bool LLDir::setCacheDir(const std::string &path){ return true; }  void LLDir::dumpCurrentDirectories() {} | 
