Introduce new LLDir::findSkinnedFilenames() method. Use as needed.

In a number of different places, for different reasons, the viewer wants to
load a UI-related file that might be overridden by a non-default skin; and
within that skin, might further be overridden by a non-default language.
Apparently, for each of those use cases, every individual developer approached
it as an entirely new problem, solving it idiosyncratically for that one case.
Not only is this a maintenance problem, but it rubs one's nose in the fact
that most such solutions consider only a subset of the relevant skin
directories.
Richard and I evolved an API intended to address all such cases: a central
LLDir method returning a list of relevant pathnames, from most general to most
localized, filtered to present only existing files; plus a couple of
convenience methods to specifically obtain the most general and most localized
available file.
There were several load-skinned-file methods (LLFloater::buildFromFile(),
LLPanel::buildFromFile() and LLUICtrlFactory::createFromFile() -- apparently
cloned-and-modified from each other) that contained funky bolted-on logic to
output the loaded data to an optional passed LLXMLNodePtr param. The trouble
is that passing that param forced each of these methods to subvert its normal
search: specifically for that case, it needed to find the baseline XML file
instead of the localized one. Richard agreed that for the intended usage
(reformatting XML files) we should use XML schema instead, and that the hacky
functionality should be removed. Remove it. Also remove
LLUICtrlFactory::getLocalizedXMLNode(), only used for those three special cases.
Some callers explicitly passed the optional LLXMLNodePtr param as NULL. Remove
that.
Remove LLFloaterUIPreview::displayFloater(save) param, which relied on the
optional output LLXMLNodePtr param. Make onClickSaveFloater() and
onClickSaveAll() emit popupAndPrintWarning() about discontinued functionality.
Recast LLFloater::buildFromFile(), LLPanel::buildFromFile(),
LLUICtrlFactory::createFromFile(), LLNotifications::loadTemplates(),
LLUI::locateSkin(), LLFontRegistry::parseFontInfo(),
LLUIColorTable::loadFromSettings(), LLUICtrlFactory::loadWidgetTemplate(),
LLUICtrlFactory::getLayeredXMLNode(), LLUIImageList::initFromFile(),
LLAppViewer::launchUpdater() and LLMediaCtrl::navigateToLocalPage() to use
findSkinnedFilenames(). (Is LLAppViewer::launchUpdater() ever called any more?
Apparently so -- though the linux-updater.bin logic to process the relevant
command-line switch has been disabled. Shrug.) (Is
LLMediaCtrl::navigateToLocalPage() ever used?? If so, why?)
Remove LLUI::setupPaths(), getXUIPaths(), getSkinPath() and
getLocalizedSkinPath(). Remove the skins/paths.xml file read by setupPaths().
The only configuration it contained was the pair of partial paths "xui/en" and
"xui/[LANGUAGE]" -- hardly likely to change. getSkinPath() specifically
returned the first of these, while getLocalizedSkinPath() specifically
returned the second. This knowledge is now embedded in findSkinnedFilenames().
Also remove paths.xml from viewer_manifest.py.
Remove injected xui_paths from LLFontGL::initClass() and
LLFontRegistry::LLFontRegistry(). These are no longer needed since
LLFontRegistry can now directly consult LLDir for its path search. Stop
passing LLUI::getXUIPaths() to LLFontGL::initClass() in LLViewerWindow's
constructor and initFonts() method.
Add LLDir::append() and add() methods for the simple task of combining two
path components separated by getDirDelimiter() -- but only if they're both
non-empty. Amazing how often that logic is replicated. Replace some existing
concatenations with add() or append().
New LLDir::findSkinnedFilenames() method must know current language. Allow
injecting current language by adding an LLDir::setSkinFolder(language) param,
and pass it where LLAppViewer::init() and initConfiguration() currently call
setSkinFolder(). Also add LLDir::getSkinFolder() and getLanguage() methods.
Change LLFLoaterUIPreview's LLLocalizationResetForcer helper to "forcibly
reset language" using LLDir::setSkinFolder() instead of LLUI::setupPaths().
Update LLDir stubs in lldir_stub.cpp and llupdaterservice_test.cpp.
Add LLDir::getUserDefaultSkinDir() to obtain often-overlooked possible skin
directory -- like getUserSkinDir() but with "default" in place of the current
skin name as the last path component. (However, we hope findSkinnedFilenames()
obviates most explicit use of such individual skin directory pathnames.)
Add LLDir unit tests for new findSkinnedFilenames() and add() methods -- the
latter exercises append() as well.
Tweak indra/integration_tests/llui_libtest/llui_libtest.cpp for all the above.
Notably, comment out its export_test_floaters() function, since the essential
LLFloater::buildFromFile(optional LLXMLNodePtr) functionality has been
removed. This may mean that llui_libtest.cpp has little remaining value, not
sure.
master
Nat Goodspeed 2012-10-10 14:57:43 -04:00
parent e97c06cf0c
commit 3653727e7f
38 changed files with 980 additions and 552 deletions

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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;
}

View File

@ -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;
};

View File

@ -3229,24 +3229,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::getLayeredXMLNode(filename, root))
{
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))
{
llwarns << "Couldn't parse floater from: " << LLUI::getSkinPath() + gDirUtilp->getDirDelimiter() + filename << llendl;
llwarns << "Couldn't find (or parse) floater from: " << filename << llendl;
return false;
}
@ -3271,7 +3261,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);

View File

@ -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 );

View File

@ -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;

View File

@ -1424,25 +1424,18 @@ 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(merge=true) 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", true);
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 +1445,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 +1501,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;

View File

@ -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();

View File

@ -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);

View File

@ -1836,88 +1836,39 @@ 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);
/*==========================================================================*|
// Hmm, if we got this far, previous implementation of this method would
// return this last found_file value whether or not it actually exists.
if (gDirUtilp->fileExists(found_file))
{
found_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename);
return found_file;
}
|*==========================================================================*/
return found_file;
}
}
//static
LLVector2 LLUI::getWindowSize()

View File

@ -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; }

View File

@ -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,11 @@ 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)
// pass merge=true because we want colors.xml from every skin dir
BOOST_FOREACH(std::string colors_path,
gDirUtilp->findSkinnedFilenames(LLDir::SKINBASE, "colors.xml", true))
{
result |= loadFromFilename(current_filename, mLoadedColors);
}
current_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SKIN, "colors.xml");
if(current_filename != default_filename)
{
result |= loadFromFilename(current_filename, mLoadedColors);
result |= loadFromFilename(colors_path, mLoadedColors);
}
std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "colors.xml");

View File

@ -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);
@ -152,19 +154,8 @@ static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing");
bool LLUICtrlFactory::getLayeredXMLNode(const std::string &xui_filename, LLXMLNodePtr& root)
{
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);
if (paths.empty())
{
@ -176,23 +167,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()
//-----------------------------------------------------------------------------
@ -239,8 +213,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()
@ -260,7 +236,7 @@ void LLUICtrlFactory::setCtrlParent(LLView* view, LLView* parent, S32 tab_group)
//static
std::string LLUICtrlFactory::findSkinnedFilename(const std::string& filename)
{
return gDirUtilp->findSkinnedFilename(LLUI::getSkinPath(), filename);
return gDirUtilp->findSkinnedFilenameBaseLang(LLDir::XUI, filename);
}
//static

View File

@ -169,7 +169,7 @@ 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;
@ -178,23 +178,13 @@ public:
{
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;
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);
@ -223,7 +213,6 @@ 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);
private:
//NOTE: both friend declarations are necessary to keep both gcc and msvc happy

View File

@ -41,6 +41,12 @@
#include "lluuid.h"
#include "lldiriterator.h"
#include "stringize.h"
#include <boost/foreach.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <algorithm>
#include <iomanip>
#if LL_WINDOWS
#include "lldir_win32.h"
@ -58,6 +64,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 +84,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 +111,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 +283,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,21 +311,26 @@ 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::getUserDefaultSkinDir() const
{
return mUserDefaultSkinDir;
}
const std::string &LLDir::getUserSkinDir() const
{
return mUserSkinDir;
}
const std::string& LLDir::getDefaultSkinDir() const
{
return mDefaultSkinDir;
}
const std::string LLDir::getSkinBaseDir() const
{
return mSkinBaseDir;
@ -323,6 +341,41 @@ const std::string &LLDir::getLLPluginDir() const
return mLLPluginDir;
}
static std::string ELLPathToString(ELLPath location)
{
typedef std::map<ELLPath, const char*> ELLPathMap;
#define ENT(symbol) ELLPathMap::value_type(symbol, #symbol)
static ELLPathMap::value_type init[] =
{
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
static const ELLPathMap sMap(boost::begin(init), boost::end(init));
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 +396,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 +412,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 +441,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 +465,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 +473,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;
}
else
{
expanded_filename.assign("");
llwarns << ELLPathToString(location)
<< ", '" << subdir1 << "', '" << subdir2 << "', '" << in_filename
<< "': prefix is empty, possible bad filename" << llendl;
}
//llinfos << "*** EXPANDED FILENAME: <" << expanded_filename << ">" << llendl;
std::string expanded_filename = add(add(prefix, subdir1), subdir2);
if (expanded_filename.empty() && in_filename.empty())
{
return "";
}
// 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 +542,168 @@ 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,
bool merge) const
{
return findSkinnedFilename("", "", filename);
// This implementation is basically just as described in the declaration comments.
std::vector<std::string> found(findSkinnedFilenames(subdir, filename, merge));
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,
bool merge) const
{
return findSkinnedFilename("", subdir, filename);
// This implementation is basically just as described in the declaration comments.
std::vector<std::string> found(findSkinnedFilenames(subdir, filename, merge));
if (found.empty())
{
return "";
}
return found.back();
}
std::string LLDir::findSkinnedFilename(const std::string &subdir1, const std::string &subdir2, const std::string &filename) const
std::vector<std::string> LLDir::findSkinnedFilenames(const std::string& subdir,
const std::string& filename,
bool merge) const
{
// generate subdirectory path fragment, e.g. "/foo/bar", "/foo", ""
std::string subdirs = ((subdir1.empty() ? "" : mDirDelimiter) + subdir1)
+ ((subdir2.empty() ? "" : mDirDelimiter) + subdir2);
// Recognize subdirs that have no localization.
static const char* sUnlocalizedData[] =
{
"", // top-level directory not localized
"textures" // textures not localized
};
static const std::set<std::string> sUnlocalized(boost::begin(sUnlocalizedData),
boost::end(sUnlocalizedData));
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
LL_DEBUGS("LLDir") << "subdir '" << subdir << "', filename '" << filename
<< "', merge " << std::boolalpha << merge << LL_ENDL;
std::string found_file = findFile(filename, search_paths);
return found_file;
// 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."
typedef std::map<std::string, std::string> LocalizedMap;
static LocalizedMap sLocalized;
// Check whether we've already discovered if this subdir is localized.
LocalizedMap::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(LocalizedMap::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(LocalizedMap::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(LocalizedMap::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(LocalizedMap::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);
}
}
// Code below relies on subsubdirs not being empty: more specifically, on
// front() being valid. There may or may not be additional entries, but we
// have at least one. For an unlocalized subdir, it's the only one; for a
// localized subdir, it's the default one.
llassert(! subsubdirs.empty());
// Build results vector.
std::vector<std::string> results;
BOOST_FOREACH(std::string skindir, mSearchSkinDirs)
{
std::string subdir_path(add(skindir, subdir));
// Does subdir_path/subsubdirs[0]/filename exist? If there's more than
// one entry in subsubdirs, the first is the default language ("en"),
// the second is the current language. A skin that contains
// subdir/language/filename without also containing subdir/en/filename
// is ill-formed: skip any such skin. So to decide whether to keep
// this skin dir or skip it, we need only check for the existence of
// the first subsubdir entry ("en" or only).
std::string subsubdir_path(add(add(subdir_path, subsubdirs.front()), filename));
if (! fileExists(subsubdir_path))
continue;
// Here the desired filename exists in the first subsubdir. That means
// this is a skindir we want to record in results. But if the caller
// passed merge=false, we must discard all previous skindirs.
if (! merge)
{
results.clear();
}
// Now add every subsubdir in which filename exists. We already know
// it exists in the first one.
results.push_back(subsubdir_path);
// Append all remaining subsubdirs in which filename exists.
for (std::vector<std::string>::const_iterator ssdi(subsubdirs.begin() + 1), ssdend(subsubdirs.end());
ssdi != ssdend; ++ssdi)
{
subsubdir_path = add(add(subdir_path, *ssdi), filename);
if (fileExists(subsubdir_path))
{
results.push_back(subsubdir_path);
}
}
}
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 +714,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 +750,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 +782,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 +791,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 +857,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 +890,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)
{

View File

@ -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 &getSkinDir() const; // User-specified skin folder.
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 &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 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,59 @@ 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()
/**
* 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 merge 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 merge=false. 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 merge=true. 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,
bool merge=false) 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,
bool merge=false) 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,
bool merge=false) const;
// random filename in common temporary directory
std::string getTempFilename() const;
@ -132,15 +182,30 @@ 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);
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 +223,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
};

View File

@ -27,11 +27,161 @@
#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[] =
{
"install/skins/default/colors.xml",
"install/skins/default/xui/en/strings.xml",
"install/skins/default/xui/fr/strings.xml",
"install/skins/default/xui/en/floater.xml",
"install/skins/default/xui/fr/floater.xml",
"install/skins/default/xui/en/newfile.xml",
"install/skins/default/xui/fr/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/default/future/somefile.txt",
"install/skins/steam/colors.xml",
"install/skins/steam/xui/en/strings.xml",
"install/skins/steam/xui/fr/strings.xml",
"install/skins/steam/textures/only_steam.jpeg",
"user/skins/default/colors.xml",
"user/skins/default/xui/en/strings.xml",
"user/skins/default/xui/fr/strings.xml",
// This is an attempted override that doesn't work: for a
// localized subdir, a skin must have subdir/en/filename as well
// as subdir/language/filename.
"user/skins/default/xui/fr/floater.xml",
// This is an override that only specifies the "en" version
"user/skins/default/xui/en/newfile.xml",
"user/skins/default/textures/only_user_default.jpeg",
"user/skins/steam/colors.xml",
"user/skins/steam/xui/en/strings.xml",
"user/skins/steam/xui/fr/strings.xml",
"user/skins/steam/textures/only_user_steam.jpeg"
};
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 +569,192 @@ 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", true),
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 to be unlocalized.
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", true),
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", true),
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", true),
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")));
// The most specific skin for our dummy floater.xml is the installed
// default. Although we have a user xui/fr/floater.xml, we would also
// need a xui/en/floater.xml file to consider the user skin for this.
ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "floater.xml"),
vec(list_of
("install/skins/default/xui/en/floater.xml")
("install/skins/default/xui/fr/floater.xml")));
// The user override for the default skin does define newfile.xml, but
// only an "en" version, not a "fr" version as well. Nonetheless
// that's the most specific skin we have, regardless of the existence
// of a "fr" version in the installed default skin.
ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "newfile.xml"),
vec(list_of("user/skins/default/xui/en/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 the user default skins strings.xml has only a "fr" override
// but not a "zh" override, 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", true),
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")));
// merge=false
ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml"),
vec(list_of("user/skins/steam/xui/en/strings.xml")));
// pass merge=true to request this filename in all relevant skins
ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true),
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 merge=true to request this filename in all relevant skins
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 merge=true to request this filename in all relevant skins
ensure_equals(lldir.findSkinnedFilenames(LLDir::XUI, "strings.xml", true),
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");
}
}

View File

@ -122,7 +122,6 @@
#include <boost/algorithm/string.hpp>
#if LL_WINDOWS
# include <share.h> // For _SH_DENYWR in initMarkerFile
#else
@ -684,7 +683,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();
@ -768,12 +767,16 @@ bool LLAppViewer::init()
&LLUI::sGLScaleFactor);
LL_INFOS("InitInfo") << "UI initialized." << LL_ENDL ;
// Setup paths and LLTrans after LLUI::initClass has been called.
LLUI::setupPaths();
// 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.
LLTransUtil::parseStrings("strings.xml", default_trans_args);
LLTransUtil::parseLanguageStrings("language_settings.xml");
// 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 ;
@ -2242,8 +2245,7 @@ bool LLAppViewer::initConfiguration()
OSMessageBox(msg.str(),LLStringUtil::null,OSMB_OK);
return false;
}
LLUI::setupPaths(); // setup paths for LLTrans based on settings files only
LLTransUtil::parseStrings("strings.xml", default_trans_args);
LLTransUtil::parseLanguageStrings("language_settings.xml");
// - set procedural settings
@ -2559,13 +2561,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"))
{
@ -3589,8 +3593,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))
@ -3606,8 +3609,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++;
@ -3838,7 +3841,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)
@ -5149,20 +5152,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(); )
BOOST_FOREACH(std::string this_skin_path, xui_path_vec)
{
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
// 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 =

View File

@ -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));
}
}

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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);
}
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -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)

View File

@ -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);

View File

@ -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);

View File

@ -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))
{

View File

@ -1583,49 +1583,42 @@ 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");
LLXMLNodePtr root;
if (!LLXMLNode::parseFile(base_file_path, root, NULL))
// Look for textures.xml in all the right places. Pass merge=true because
// we want to overlay textures.xml from all the skins directories.
std::vector<std::string> textures_paths =
gDirUtilp->findSkinnedFilenames(LLDir::TEXTURES, "textures.xml", true);
std::vector<std::string>::const_iterator pi(textures_paths.begin()), pend(textures_paths.end());
if (pi == pend)
{
llwarns << "Unable to parse UI image list file " << base_file_path << llendl;
llwarns << "No textures.xml found in skins directories" << llendl;
return false;
}
// The first (most generic) file gets special validations
LLXMLNodePtr root;
if (!LLXMLNode::parseFile(*pi, root, NULL))
{
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);
parser.readXUI(root, images, *pi);
// 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))
// add components defined in the rest of the skin paths
while (++pi != pend)
{
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);
LLXMLNodePtr update_root;
if (LLXMLNode::parseFile(*pi, update_root, NULL))
{
parser.readXUI(update_root, images, *pi);
}
}
if (!images.validateBlock()) return false;

View File

@ -1685,8 +1685,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;
@ -4757,8 +4756,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();
}

View File

@ -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;

View File

@ -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;

View File

@ -1,10 +0,0 @@
<paths>
<directory>
<subdir>xui</subdir>
<subdir>en</subdir>
</directory>
<directory>
<subdir>xui</subdir>
<subdir>[LANGUAGE]</subdir>
</directory>
</paths>

View File

@ -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() {}

View File

@ -114,7 +114,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")

View File

@ -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() {}