summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorAdam Moss <moss@lindenlab.com>2009-08-21 21:52:21 +0000
committerAdam Moss <moss@lindenlab.com>2009-08-21 21:52:21 +0000
commitc3cbd049859c058526ae9a07a5cbfa7e51085943 (patch)
tree80f74a033a2f56f465cd4a67fa3e11a00bc18248 /indra
parent87be6648d50efc55785e2298a9ed6ec0546da564 (diff)
svn merge -r130238:130240 svn+ssh://svn.lindenlab.com/svn/linden/branches/linux-updater-6
QAR-1771 Linux Viewer Autoupdater + XUI-parse refactoring
Diffstat (limited to 'indra')
-rw-r--r--indra/CMakeLists.txt4
-rw-r--r--indra/cmake/LLXUIXML.cmake7
-rw-r--r--indra/integration_tests/llui_libtest/CMakeLists.txt2
-rw-r--r--indra/integration_tests/llui_libtest/llui_libtest.cpp6
-rw-r--r--indra/linux_updater/CMakeLists.txt58
-rw-r--r--indra/linux_updater/linux_updater.cpp818
-rw-r--r--indra/llui/CMakeLists.txt12
-rw-r--r--indra/llui/lltransutil.cpp67
-rw-r--r--indra/llui/lltransutil.h51
-rw-r--r--indra/llui/lluictrlfactory.cpp934
-rw-r--r--indra/llui/lluictrlfactory.h116
-rw-r--r--indra/llwindow/llwindowsdl.cpp6
-rw-r--r--indra/llxuixml/CMakeLists.txt45
-rw-r--r--indra/llxuixml/llinitparam.cpp524
-rw-r--r--indra/llxuixml/llinitparam.h1822
-rw-r--r--indra/llxuixml/llregistry.h347
-rw-r--r--indra/llxuixml/lltrans.cpp171
-rw-r--r--indra/llxuixml/lltrans.h111
-rw-r--r--indra/llxuixml/lluicolor.cpp71
-rw-r--r--indra/llxuixml/lluicolor.h45
-rw-r--r--indra/llxuixml/llxuiparser.cpp968
-rw-r--r--indra/llxuixml/llxuiparser.h174
-rw-r--r--indra/newview/CMakeLists.txt4
-rw-r--r--indra/newview/linux_tools/client-readme.txt10
-rwxr-xr-xindra/newview/linux_tools/handle_secondlifeprotocol.sh2
-rwxr-xr-xindra/newview/linux_tools/install.sh106
-rwxr-xr-xindra/newview/linux_tools/refresh_desktop_app_entry.sh36
-rwxr-xr-xindra/newview/linux_tools/register_secondlifeprotocol.sh4
-rwxr-xr-xindra/newview/linux_tools/wrapper.sh12
-rw-r--r--indra/newview/llappviewer.cpp9
-rw-r--r--indra/newview/llappviewerlinux.cpp2
-rw-r--r--indra/newview/llstartup.cpp78
-rw-r--r--indra/newview/llviewertexturelist.cpp5
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml39
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml35
-rwxr-xr-xindra/newview/viewer_manifest.py14
36 files changed, 5609 insertions, 1106 deletions
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt
index 1010b199a1..a5ad24815c 100644
--- a/indra/CMakeLists.txt
+++ b/indra/CMakeLists.txt
@@ -65,10 +65,12 @@ add_custom_target(viewer)
if (VIEWER)
add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger)
add_subdirectory(${LIBS_OPEN_PREFIX}llui)
+ add_subdirectory(${LIBS_OPEN_PREFIX}llxuixml)
if (LINUX)
add_subdirectory(${VIEWER_PREFIX}linux_crash_logger)
- add_dependencies(viewer linux-crash-logger-strip-target)
+ add_subdirectory(${VIEWER_PREFIX}linux_updater)
+ add_dependencies(viewer linux-crash-logger-strip-target linux-updater)
elseif (DARWIN)
add_subdirectory(${VIEWER_PREFIX}mac_crash_logger)
add_subdirectory(${VIEWER_PREFIX}mac_updater)
diff --git a/indra/cmake/LLXUIXML.cmake b/indra/cmake/LLXUIXML.cmake
new file mode 100644
index 0000000000..b8bfe48c77
--- /dev/null
+++ b/indra/cmake/LLXUIXML.cmake
@@ -0,0 +1,7 @@
+# -*- cmake -*-
+
+set(LLXUIXML_INCLUDE_DIRS
+ ${LIBS_OPEN_DIR}/llxuixml
+ )
+
+set(LLXUIXML_LIBRARIES llxuixml)
diff --git a/indra/integration_tests/llui_libtest/CMakeLists.txt b/indra/integration_tests/llui_libtest/CMakeLists.txt
index 88564c6085..1ccdb0f20b 100644
--- a/indra/integration_tests/llui_libtest/CMakeLists.txt
+++ b/indra/integration_tests/llui_libtest/CMakeLists.txt
@@ -16,6 +16,7 @@ include(LLWindow)
include(LLUI)
include(LLVFS) # ugh, needed for LLDir
include(LLXML)
+include(LLXUIXML)
include(Linking)
# include(Tut)
@@ -29,6 +30,7 @@ include_directories(
${LLVFS_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
+ ${LLXUIXML_INCLUDE_DIRS}
)
set(llui_libtest_SOURCE_FILES
diff --git a/indra/integration_tests/llui_libtest/llui_libtest.cpp b/indra/integration_tests/llui_libtest/llui_libtest.cpp
index 3d433fdfdc..3631761c93 100644
--- a/indra/integration_tests/llui_libtest/llui_libtest.cpp
+++ b/indra/integration_tests/llui_libtest/llui_libtest.cpp
@@ -43,7 +43,7 @@
#include "llfloater.h"
#include "llfontfreetype.h"
#include "llfontgl.h"
-#include "lltrans.h"
+#include "lltransutil.h"
#include "llui.h"
#include "lluictrlfactory.h"
@@ -154,8 +154,8 @@ void init_llui()
// Otherwise we get translation warnings when setting up floaters
// (tooltips for buttons)
std::set<std::string> default_args;
- LLTrans::parseStrings("strings.xml", default_args);
- LLTrans::parseLanguageStrings("language_settings.xml");
+ LLTransUtil::parseStrings("strings.xml", default_args);
+ LLTransUtil::parseLanguageStrings("language_settings.xml");
LLFontManager::initClass();
// Creating widgets apparently requires fonts to be initialized,
diff --git a/indra/linux_updater/CMakeLists.txt b/indra/linux_updater/CMakeLists.txt
new file mode 100644
index 0000000000..9fe32ecb46
--- /dev/null
+++ b/indra/linux_updater/CMakeLists.txt
@@ -0,0 +1,58 @@
+# -*- cmake -*-
+
+project(linux_updater)
+
+include(00-Common)
+include(CURL)
+include(CARes)
+include(OpenSSL)
+include(UI)
+include(LLCommon)
+include(LLVFS)
+include(LLXML)
+include(LLXUIXML)
+include(Linking)
+
+include_directories(
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLVFS_INCLUDE_DIRS}
+ ${LLXML_INCLUDE_DIRS}
+ ${LLXUIXML_INCLUDE_DIRS}
+ ${CURL_INCLUDE_DIRS}
+ ${CARES_INCLUDE_DIRS}
+ ${OPENSSL_INCLUDE_DIRS}
+ ${UI_INCLUDE_DIRS}
+ )
+
+set(linux_updater_SOURCE_FILES linux_updater.cpp)
+
+set(linux_updater_HEADER_FILES CMakeLists.txt)
+
+set_source_files_properties(${linux_updater_HEADER_FILES}
+ PROPERTIES HEADER_FILES_ONLY TRUE)
+
+list(APPEND linux_updater_SOURCE_FILES ${linux_updater_HEADER_FILES})
+
+add_executable(linux-updater ${linux_updater_SOURCE_FILES})
+
+target_link_libraries(linux-updater
+ ${CURL_LIBRARIES}
+ ${CARES_LIBRARIES}
+ ${OPENSSL_LIBRARIES}
+ ${CRYPTO_LIBRARIES}
+ ${UI_LIBRARIES}
+ ${LLXML_LIBRARIES}
+ ${LLXUIXML_LIBRARIES}
+ ${LLVFS_LIBRARIES}
+ ${LLCOMMON_LIBRARIES}
+ )
+
+add_custom_command(
+ OUTPUT linux-updater-stripped
+ COMMAND strip
+ ARGS --strip-debug -o linux-updater-stripped linux-updater
+ DEPENDS linux-updater
+ )
+
+add_custom_target(linux-updater-strip-target ALL
+ DEPENDS linux-updater-stripped)
diff --git a/indra/linux_updater/linux_updater.cpp b/indra/linux_updater/linux_updater.cpp
new file mode 100644
index 0000000000..acc60d42bf
--- /dev/null
+++ b/indra/linux_updater/linux_updater.cpp
@@ -0,0 +1,818 @@
+/**
+ * @file linux_updater.cpp
+ * @author Kyle Ambroff <ambroff@lindenlab.com>, Tofu Linden
+ * @brief Viewer update program for unix platforms that support GTK+
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ *
+ * Copyright (c) 2008, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "linden_common.h"
+#include "llerrorcontrol.h"
+#include "llfile.h"
+#include "lldir.h"
+#include "llxmlnode.h"
+#include "lltrans.h"
+
+#include <curl/curl.h>
+
+extern "C" {
+#include <gtk/gtk.h>
+}
+
+const guint UPDATE_PROGRESS_TIMEOUT = 100;
+const guint UPDATE_PROGRESS_TEXT_TIMEOUT = 1000;
+const guint ROTATE_IMAGE_TIMEOUT = 8000;
+
+typedef struct _updater_app_state {
+ std::string app_name;
+ std::string url;
+ std::string image_dir;
+ std::string dest_dir;
+ std::string strings_dirs;
+ std::string strings_file;
+
+ GtkWidget *window;
+ GtkWidget *progress_bar;
+ GtkWidget *image;
+
+ double progress_value;
+ bool activity_mode;
+
+ guint image_rotation_timeout_id;
+ guint progress_update_timeout_id;
+ guint update_progress_text_timeout_id;
+
+ bool failure;
+} UpdaterAppState;
+
+// List of entries from strings.xml to always replace
+static std::set<std::string> default_trans_args;
+void init_default_trans_args()
+{
+ default_trans_args.insert("SECOND_LIFE"); // World
+ default_trans_args.insert("SECOND_LIFE_VIEWER");
+ default_trans_args.insert("SECOND_LIFE_GRID");
+ default_trans_args.insert("SECOND_LIFE_SUPPORT");
+}
+
+bool translate_init(std::string comma_delim_path_list,
+ std::string base_xml_name)
+{
+ init_default_trans_args();
+
+ // extract paths string vector from comma-delimited flat string
+ std::vector<std::string> paths;
+ LLStringUtil::getTokens(comma_delim_path_list, paths); // split over ','
+
+ // suck the translation xml files into memory
+ LLXMLNodePtr root;
+ bool success = LLXMLNode::getLayeredXMLNode(base_xml_name, root, paths);
+ if (!success)
+ {
+ // couldn't load string table XML
+ return false;
+ }
+ else
+ {
+ // get those strings out of the XML
+ LLTrans::parseStrings(root, default_trans_args);
+ return true;
+ }
+}
+
+
+void updater_app_ui_init(void);
+void updater_app_quit(UpdaterAppState *app_state);
+void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state);
+std::string next_image_filename(std::string& image_path);
+void display_error(GtkWidget *parent, std::string title, std::string message);
+BOOL install_package(std::string package_file, std::string destination);
+BOOL spawn_viewer(UpdaterAppState *app_state);
+
+extern "C" {
+ void on_window_closed(GtkWidget *sender, gpointer state);
+ gpointer worker_thread_cb(gpointer *data);
+ int download_progress_cb(gpointer data, double t, double d, double utotal, double ulnow);
+ gboolean rotate_image_cb(gpointer data);
+ gboolean progress_update_timeout(gpointer data);
+ gboolean update_progress_text_timeout(gpointer data);
+}
+
+void updater_app_ui_init(UpdaterAppState *app_state)
+{
+ GtkWidget *vbox;
+ GtkWidget *summary_label;
+ GtkWidget *description_label;
+ GtkWidget *frame;
+
+ llassert(app_state != NULL);
+
+ // set up window and main container
+ std::string window_title = LLTrans::getString("UpdaterWindowTitle");
+ app_state->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(app_state->window),
+ window_title.c_str());
+ gtk_window_set_resizable(GTK_WINDOW(app_state->window), FALSE);
+ gtk_window_set_position(GTK_WINDOW(app_state->window),
+ GTK_WIN_POS_CENTER_ALWAYS);
+
+ gtk_container_set_border_width(GTK_CONTAINER(app_state->window), 12);
+ g_signal_connect(G_OBJECT(app_state->window), "delete-event",
+ G_CALLBACK(on_window_closed), app_state);
+
+ vbox = gtk_vbox_new(FALSE, 6);
+ gtk_container_add(GTK_CONTAINER(app_state->window), vbox);
+
+ // set top label
+ std::ostringstream label_ostr;
+ label_ostr << "<big><b>"
+ << LLTrans::getString("UpdaterNowUpdating")
+ << "</b></big>";
+
+ summary_label = gtk_label_new(NULL);
+ gtk_label_set_use_markup(GTK_LABEL(summary_label), TRUE);
+ gtk_label_set_markup(GTK_LABEL(summary_label),
+ label_ostr.str().c_str());
+ gtk_misc_set_alignment(GTK_MISC(summary_label), 0, 0.5);
+ gtk_box_pack_start(GTK_BOX(vbox), summary_label, FALSE, FALSE, 0);
+
+ // create the description label
+ description_label = gtk_label_new(LLTrans::getString("UpdaterUpdatingDescriptive").c_str());
+ gtk_label_set_line_wrap(GTK_LABEL(description_label), TRUE);
+ gtk_misc_set_alignment(GTK_MISC(description_label), 0, 0.5);
+ gtk_box_pack_start(GTK_BOX(vbox), description_label, FALSE, FALSE, 0);
+
+ // If an image path has been set, load the background images
+ if (!app_state->image_dir.empty()) {
+ frame = gtk_frame_new(NULL);
+ gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_IN);
+ gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0);
+
+ // load the first image
+ app_state->image = gtk_image_new_from_file
+ (next_image_filename(app_state->image_dir).c_str());
+ gtk_widget_set_size_request(app_state->image, 340, 310);
+ gtk_container_add(GTK_CONTAINER(frame), app_state->image);
+
+ // rotate the images every 5 seconds
+ app_state->image_rotation_timeout_id = g_timeout_add
+ (ROTATE_IMAGE_TIMEOUT, rotate_image_cb, app_state);
+ }
+
+ // set up progress bar, and update it roughly every 1/10 of a second
+ app_state->progress_bar = gtk_progress_bar_new();
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
+ LLTrans::getString("UpdaterProgressBarTextWithEllipses").c_str());
+ gtk_box_pack_start(GTK_BOX(vbox),
+ app_state->progress_bar, FALSE, TRUE, 0);
+ app_state->progress_update_timeout_id = g_timeout_add
+ (UPDATE_PROGRESS_TIMEOUT, progress_update_timeout, app_state);
+ app_state->update_progress_text_timeout_id = g_timeout_add
+ (UPDATE_PROGRESS_TEXT_TIMEOUT, update_progress_text_timeout, app_state);
+
+ gtk_widget_show_all(app_state->window);
+}
+
+gboolean rotate_image_cb(gpointer data)
+{
+ UpdaterAppState *app_state;
+ std::string filename;
+
+ llassert(data != NULL);
+ app_state = (UpdaterAppState *) data;
+
+ filename = next_image_filename(app_state->image_dir);
+
+ gdk_threads_enter();
+ gtk_image_set_from_file(GTK_IMAGE(app_state->image), filename.c_str());
+ gdk_threads_leave();
+
+ return TRUE;
+}
+
+std::string next_image_filename(std::string& image_path)
+{
+ std::string image_filename;
+ gDirUtilp->getNextFileInDir(image_path, "/*.jpg", image_filename, true);
+ return image_path + "/" + image_filename;
+}
+
+void on_window_closed(GtkWidget *sender, gpointer data)
+{
+ UpdaterAppState *app_state;
+
+ llassert(data != NULL);
+ app_state = (UpdaterAppState *) data;
+
+ updater_app_quit(app_state);
+}
+
+void updater_app_quit(UpdaterAppState *app_state)
+{
+ if (app_state != NULL)
+ {
+ g_source_remove(app_state->progress_update_timeout_id);
+
+ if (!app_state->image_dir.empty())
+ {
+ g_source_remove(app_state->image_rotation_timeout_id);
+ }
+ }
+
+ gtk_main_quit();
+}
+
+void display_error(GtkWidget *parent, std::string title, std::string message)
+{
+ GtkWidget *dialog;
+
+ dialog = gtk_message_dialog_new(GTK_WINDOW(parent),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ message.c_str());
+ gtk_window_set_title(GTK_WINDOW(dialog), title.c_str());
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+}
+
+gpointer worker_thread_cb(gpointer data)
+{
+ UpdaterAppState *app_state;
+ CURL *curl;
+ CURLcode result;
+ FILE *package_file;
+ GError *error = NULL;
+ char *tmp_filename = NULL;
+ int fd;
+
+ //g_return_val_if_fail (data != NULL, NULL);
+ app_state = (UpdaterAppState *) data;
+
+ try {
+ // create temporary file to store the package.
+ fd = g_file_open_tmp
+ ("secondlife-update-XXXXXX", &tmp_filename, &error);
+ if (error != NULL)
+ {
+ llerrs << "Unable to create temporary file: "
+ << error->message
+ << llendl;
+
+ g_error_free(error);
+ throw 0;
+ }
+
+ package_file = fdopen(fd, "wb");
+ if (package_file == NULL)
+ {
+ llerrs << "Failed to create temporary file: "
+ << tmp_filename
+ << llendl;
+
+ gdk_threads_enter();
+ display_error(app_state->window,
+ LLTrans::getString("UpdaterFailDownloadTitle"),
+ LLTrans::getString("UpdaterFailUpdateDescriptive"));
+ gdk_threads_leave();
+ throw 0;
+ }
+
+ // initialize curl and start downloading the package
+ llinfos << "Downloading package: " << app_state->url << llendl;
+
+ curl = curl_easy_init();
+ if (curl == NULL)
+ {
+ llerrs << "Failed to initialize libcurl" << llendl;
+
+ gdk_threads_enter();
+ display_error(app_state->window,
+ LLTrans::getString("UpdaterFailDownloadTitle"),
+ LLTrans::getString("UpdaterFailUpdateDescriptive"));
+ gdk_threads_leave();
+ throw 0;
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, app_state->url.c_str());
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, TRUE);
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, TRUE);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, package_file);
+ curl_easy_setopt(curl, CURLOPT_NOPROGRESS, FALSE);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,
+ &download_progress_cb);
+ curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, app_state);
+
+ result = curl_easy_perform(curl);
+ fclose(package_file);
+ curl_easy_cleanup(curl);
+
+ if (result)
+ {
+ llerrs << "Failed to download update: "
+ << app_state->url
+ << llendl;
+
+ gdk_threads_enter();
+ display_error(app_state->window,
+ LLTrans::getString("UpdaterFailDownloadTitle"),
+ LLTrans::getString("UpdaterFailUpdateDescriptive"));
+ gdk_threads_leave();
+
+ throw 0;
+ }
+
+ // now pulse the progres bar back and forth while the package is
+ // being unpacked
+ gdk_threads_enter();
+ std::string installing_msg = LLTrans::getString("UpdaterNowInstalling");
+ gtk_progress_bar_set_text(
+ GTK_PROGRESS_BAR(app_state->progress_bar),
+ installing_msg.c_str());
+ app_state->activity_mode = TRUE;
+ gdk_threads_leave();
+
+ // *TODO: if the destination is not writable, terminate this
+ // thread and show file chooser?
+ if (!install_package(tmp_filename, app_state->dest_dir))
+ {
+ llwarns << "Failed to install package to destination: "
+ << app_state->dest_dir
+ << llendl;
+
+ gdk_threads_enter();
+ display_error(app_state->window,
+ LLTrans::getString("UpdaterFailInstallTitle"),
+ LLTrans::getString("UpdaterFailUpdateDescriptive"));
+ //"Failed to update " + app_state->app_name,
+ gdk_threads_leave();
+ throw 0;
+ }
+
+ // try to spawn the new viewer
+ if (!spawn_viewer(app_state))
+ {
+ llwarns << "Viewer was not installed properly in : "
+ << app_state->dest_dir
+ << llendl;
+
+ gdk_threads_enter();
+ display_error(app_state->window,
+ LLTrans::getString("UpdaterFailStartTitle"),
+ LLTrans::getString("UpdaterFailUpdateDescriptive"));
+ gdk_threads_leave();
+ throw 0;
+ }
+ }
+ catch (...)
+ {
+ app_state->failure = TRUE;
+ }
+
+ // FIXME: delete package file also if delete-event is raised on window
+ if (tmp_filename != NULL)
+ {
+ if (gDirUtilp->fileExists(tmp_filename))
+ {
+ LLFile::remove(tmp_filename);
+ }
+ }
+
+ gdk_threads_enter();
+ updater_app_quit(app_state);
+ gdk_threads_leave();
+
+ return NULL;
+}
+
+
+gboolean less_anal_gspawnsync(gchar **argv,
+ gchar **stderr_output,
+ gint *child_exit_status,
+ GError **spawn_error)
+{
+ // store current SIGCHLD handler if there is one, replace with default
+ // handler to make glib happy
+ struct sigaction sigchld_backup;
+ struct sigaction sigchld_appease_glib;
+ sigchld_appease_glib.sa_handler = SIG_DFL;
+ sigemptyset(&sigchld_appease_glib.sa_mask);
+ sigchld_appease_glib.sa_flags = 0;
+ sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
+
+ gboolean rtn = g_spawn_sync(NULL,
+ argv,
+ NULL,
+ (GSpawnFlags) (G_SPAWN_STDOUT_TO_DEV_NULL),
+ NULL,
+ NULL,
+ NULL,
+ stderr_output,
+ child_exit_status,
+ spawn_error);
+
+ // restore SIGCHLD handler
+ sigaction(SIGCHLD, &sigchld_backup, NULL);
+
+ return rtn;
+}
+
+
+// perform a rename, or perform a (prompted) root rename if that fails
+int
+rename_with_sudo_fallback(const std::string& filename, const std::string& newname)
+{
+ int rtncode = ::rename(filename.c_str(), newname.c_str());
+ lldebugs << "rename result is: " << rtncode << " / " << errno << llendl;
+ if (rtncode && (EACCES == errno || EPERM == errno || EXDEV == errno))
+ {
+ llinfos << "Permission problem in rename, or moving between different mount points. Retrying as a mv under a sudo." << llendl;
+ // failed due to permissions, try again as a gksudo or kdesu mv wrapper hack
+ char *sudo_cmd = NULL;
+ sudo_cmd = g_find_program_in_path("gksudo");
+ if (!sudo_cmd)
+ {
+ sudo_cmd = g_find_program_in_path("kdesu");
+ }
+ if (sudo_cmd)
+ {
+ char *mv_cmd = NULL;
+ mv_cmd = g_find_program_in_path("mv");
+ if (mv_cmd)
+ {
+ char *src_string_copy = g_strdup(filename.c_str());
+ char *dst_string_copy = g_strdup(newname.c_str());
+ char* argv[] =
+ {
+ sudo_cmd,
+ mv_cmd,
+ src_string_copy,
+ dst_string_copy,
+ NULL
+ };
+
+ gchar *stderr_output = NULL;
+ gint child_exit_status = 0;
+ GError *spawn_error = NULL;
+ if (!less_anal_gspawnsync(argv, &stderr_output,
+ &child_exit_status, &spawn_error))
+ {
+ llwarns << "Failed to spawn child process: "
+ << spawn_error->message
+ << llendl;
+ }
+ else if (child_exit_status)
+ {
+ llwarns << "mv command failed: "
+ << (stderr_output ? stderr_output : "(no reason given)")
+ << llendl;
+ }
+ else
+ {
+ // everything looks good, clear the error code
+ rtncode = 0;
+ }
+
+ g_free(src_string_copy);
+ g_free(dst_string_copy);
+ if (spawn_error) g_error_free(spawn_error);
+ }
+ }
+ }
+ return rtncode;
+}
+
+gboolean install_package(std::string package_file, std::string destination)
+{
+ char *tar_cmd = NULL;
+ std::ostringstream command;
+
+ // Find the absolute path to the 'tar' command.
+ tar_cmd = g_find_program_in_path("tar");
+ if (!tar_cmd)
+ {
+ llerrs << "`tar' was not found in $PATH" << llendl;
+ return FALSE;
+ }
+ llinfos << "Found tar command: " << tar_cmd << llendl;
+
+ // Unpack the tarball in a temporary place first, then move it to
+ // its final destination
+ std::string tmp_dest_dir = gDirUtilp->getTempFilename();
+ if (LLFile::mkdir(tmp_dest_dir, 0744))
+ {
+ llerrs << "Failed to create directory: "
+ << destination
+ << llendl;
+
+ return FALSE;
+ }
+
+ char *package_file_string_copy = g_strdup(package_file.c_str());
+ char *tmp_dest_dir_string_copy = g_strdup(tmp_dest_dir.c_str());
+ char *argv[8] = {
+ tar_cmd,
+ "--strip", "1",
+ "-xjf",
+ package_file_string_copy,
+ "-C", tmp_dest_dir_string_copy,
+ NULL,
+ };
+
+ llinfos << "Untarring package: " << package_file << llendl;
+
+ // store current SIGCHLD handler if there is one, replace with default
+ // handler to make glib happy
+ struct sigaction sigchld_backup;
+ struct sigaction sigchld_appease_glib;
+ sigchld_appease_glib.sa_handler = SIG_DFL;
+ sigemptyset(&sigchld_appease_glib.sa_mask);
+ sigchld_appease_glib.sa_flags = 0;
+ sigaction(SIGCHLD, &sigchld_appease_glib, &sigchld_backup);
+
+ gchar *stderr_output = NULL;
+ gint child_exit_status = 0;
+ GError *untar_error = NULL;
+ if (!less_anal_gspawnsync(argv, &stderr_output,
+ &child_exit_status, &untar_error))
+ {
+ llwarns << "Failed to spawn child process: "
+ << untar_error->message
+ << llendl;
+ return FALSE;
+ }
+
+ if (child_exit_status)
+ {
+ llwarns << "Untar command failed: "
+ << (stderr_output ? stderr_output : "(no reason given)")
+ << llendl;
+ return FALSE;
+ }
+
+ g_free(tar_cmd);
+ g_free(package_file_string_copy);
+ g_free(tmp_dest_dir_string_copy);
+ g_free(stderr_output);
+ if (untar_error) g_error_free(untar_error);
+
+ // move the existing package out of the way if it exists
+ if (gDirUtilp->fileExists(destination))
+ {
+ std::string backup_dir = destination + ".backup";
+ int oldcounter = 1;
+ while (gDirUtilp->fileExists(backup_dir))
+ {
+ // find a foo.backup.N folder name that isn't taken yet
+ backup_dir = destination + ".backup." + llformat("%d", oldcounter);
+ ++oldcounter;
+ }
+
+ if (rename_with_sudo_fallback(destination, backup_dir))
+ {
+ llwarns << "Failed to move directory: '"
+ << destination << "' -> '" << backup_dir
+ << llendl;
+ return FALSE;
+ }
+ }
+
+ // The package has been unpacked in a staging directory, now we just
+ // need to move it to its destination.
+ if (rename_with_sudo_fallback(tmp_dest_dir, destination))
+ {
+ llwarns << "Failed to move installation to the destination: "
+ << destination
+ << llendl;
+ return FALSE;
+ }
+
+ // \0/ Success!
+ return TRUE;
+}
+
+gboolean progress_update_timeout(gpointer data)
+{
+ UpdaterAppState *app_state;
+
+ llassert(data != NULL);
+
+ app_state = (UpdaterAppState *) data;
+
+ gdk_threads_enter();
+ if (app_state->activity_mode)
+ {
+ gtk_progress_bar_pulse
+ (GTK_PROGRESS_BAR(app_state->progress_bar));
+ }
+ else
+ {
+ gtk_progress_set_value(GTK_PROGRESS(app_state->progress_bar),
+ app_state->progress_value);
+ }
+ gdk_threads_leave();
+
+ return TRUE;
+}
+
+gboolean update_progress_text_timeout(gpointer data)
+{
+ UpdaterAppState *app_state;
+
+ llassert(data != NULL);
+ app_state = (UpdaterAppState *) data;
+
+ if (app_state->activity_mode == TRUE)
+ {
+ // We no longer need this timeout, it will be removed.
+ return FALSE;
+ }
+
+ if (!app_state->progress_value)
+ {
+ return TRUE;
+ }
+
+ std::string progress_text = llformat((LLTrans::getString("UpdaterProgressBarText")+" (%.0f%%)").c_str(), app_state->progress_value);
+
+ gdk_threads_enter();
+ gtk_progress_bar_set_text(GTK_PROGRESS_BAR(app_state->progress_bar),
+ progress_text.c_str());
+ gdk_threads_leave();
+
+ return TRUE;
+}
+
+int download_progress_cb(gpointer data,
+ double t,
+ double d,
+ double utotal,
+ double ulnow)
+{
+ UpdaterAppState *app_state;
+
+ llassert(data != NULL);
+ app_state = (UpdaterAppState *) data;
+
+ if (t <= 0.0)
+ {
+ app_state->progress_value = 0;
+ }
+ else
+ {
+ app_state->progress_value = d * 100.0 / t;
+ }
+ return 0;
+}
+
+BOOL spawn_viewer(UpdaterAppState *app_state)
+{
+ llassert(app_state != NULL);
+
+ std::string cmd = app_state->dest_dir + "/secondlife";
+ GError *error = NULL;
+
+ // We want to spawn the Viewer on the same display as the updater app
+ gboolean success = gdk_spawn_command_line_on_screen
+ (gtk_widget_get_screen(app_state->window), cmd.c_str(), &error);
+
+ if (!success)
+ {
+ llwarns << "Failed to launch viewer: " << error->message
+ << llendl;
+ }
+
+ if (error) g_error_free(error);
+
+ return success;
+}
+
+void show_usage_and_exit()
+{
+ std::cout << "Usage: linux-updater --url URL --name NAME --dest PATH --stringsdir PATH1,PATH2 --stringsfile FILE"
+ << "[--image-dir PATH]"
+ << std::endl;
+ exit(1);
+}
+
+void parse_args_and_init(int argc, char **argv, UpdaterAppState *app_state)
+{
+ int i;
+
+ for (i = 1; i < argc; i++)
+ {
+ if ((!strcmp(argv[i], "--url")) && (++i < argc))
+ {
+ app_state->url = argv[i];
+ }
+ else if ((!strcmp(argv[i], "--name")) && (++i < argc))
+ {
+ app_state->app_name = argv[i];
+ }
+ else if ((!strcmp(argv[i], "--image-dir")) && (++i < argc))
+ {
+ app_state->image_dir = argv[i];
+ }
+ else if ((!strcmp(argv[i], "--dest")) && (++i < argc))
+ {
+ app_state->dest_dir = argv[i];
+ }
+ else if ((!strcmp(argv[i], "--stringsdir")) && (++i < argc))
+ {
+ app_state->strings_dirs = argv[i];
+ }
+ else if ((!strcmp(argv[i], "--stringsfile")) && (++i < argc))
+ {
+ app_state->strings_file = argv[i];
+ }
+ else
+ {
+ // show usage, an invalid option was given.
+ show_usage_and_exit();
+ }
+ }
+
+ if (app_state->app_name.empty()
+ || app_state->url.empty()
+ || app_state->dest_dir.empty())
+ {
+ show_usage_and_exit();
+ }
+
+ app_state->progress_value = 0.0;
+ app_state->activity_mode = FALSE;
+ app_state->failure = FALSE;
+
+ translate_init(app_state->strings_dirs, app_state->strings_file);
+}
+
+int main(int argc, char **argv)
+{
+ UpdaterAppState app_state;
+ GThread *worker_thread;
+
+ parse_args_and_init(argc, argv, &app_state);
+
+ // Initialize logger, and rename old log file
+ gDirUtilp->initAppDirs("SecondLife");
+ LLError::initForApplication
+ (gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+ std::string old_log_file = gDirUtilp->getExpandedFilename
+ (LL_PATH_LOGS, "updater.log.old");
+ std::string log_file =
+ gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "updater.log");
+ LLFile::rename(log_file, old_log_file);
+ LLError::logToFile(log_file);
+
+ // initialize gthreads and gtk+
+ if (!g_thread_supported())
+ {
+ g_thread_init(NULL);
+ gdk_threads_init();
+ }
+
+ gtk_init(&argc, &argv);
+
+ // create UI
+ updater_app_ui_init(&app_state);
+
+ //llinfos << "SAMPLE TRANSLATION IS: " << LLTrans::getString("LoginInProgress") << llendl;
+
+ // create download thread
+ worker_thread = g_thread_create
+ (GThreadFunc(worker_thread_cb), &app_state, FALSE, NULL);
+
+ gdk_threads_enter();
+ gtk_main();
+ gdk_threads_leave();
+
+ return (app_state.failure == FALSE) ? 0 : 1;
+}
diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt
index e8d95b44a5..7f71ff6a53 100644
--- a/indra/llui/CMakeLists.txt
+++ b/indra/llui/CMakeLists.txt
@@ -12,6 +12,7 @@ include(LLRender)
include(LLWindow)
include(LLVFS)
include(LLXML)
+include(LLXUIXML)
include_directories(
${LLAUDIO_INCLUDE_DIRS}
@@ -23,6 +24,7 @@ include_directories(
${LLWINDOW_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
+ ${LLXUIXML_INCLUDE_DIRS}
)
set(llui_SOURCE_FILES
@@ -44,7 +46,6 @@ set(llui_SOURCE_FILES
llfocusmgr.cpp
llfunctorregistry.cpp
lliconctrl.cpp
- llinitparam.cpp
llkeywords.cpp
lllayoutstack.cpp
lllineeditor.cpp
@@ -82,9 +83,8 @@ set(llui_SOURCE_FILES
lltextbox.cpp
lltexteditor.cpp
lltextparser.cpp
- lltrans.cpp
+ lltransutil.cpp
llui.cpp
- lluicolor.cpp
lluicolortable.cpp
lluictrl.cpp
lluictrlfactory.cpp
@@ -121,7 +121,6 @@ set(llui_HEADER_FILES
llhandle.h
llhtmlhelp.h
lliconctrl.h
- llinitparam.h
llkeywords.h
lllayoutstack.h
lllazyvalue.h
@@ -136,7 +135,6 @@ set(llui_HEADER_FILES
llpanel.h
llprogressbar.h
llradiogroup.h
- llregistry.h
llresizebar.h
llresizehandle.h
llresmgr.h
@@ -161,8 +159,7 @@ set(llui_HEADER_FILES
lltextbox.h
lltexteditor.h
lltextparser.h
- lltrans.h
- lluicolor.h
+ lltransutil.h
lluicolortable.h
lluiconstants.h
lluictrlfactory.h
@@ -191,6 +188,7 @@ target_link_libraries(llui
llwindow
llimage
llvfs # ugh, just for LLDir
+ llxuixml
llxml
llcommon # must be after llimage, llwindow, llrender
llmath
diff --git a/indra/llui/lltransutil.cpp b/indra/llui/lltransutil.cpp
new file mode 100644
index 0000000000..eaee260c7a
--- /dev/null
+++ b/indra/llui/lltransutil.cpp
@@ -0,0 +1,67 @@
+/**
+ * @file lltrans.cpp
+ * @brief LLTrans implementation
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrans.h"
+#include "lluictrlfactory.h"
+
+#include "lltransutil.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);
+ if (!success)
+ {
+ llerrs << "Couldn't load string table" << llendl;
+ return false;
+ }
+
+ return LLTrans::parseStrings(root, default_args);
+}
+
+
+bool LLTransUtil::parseLanguageStrings(const std::string& xml_filename)
+{
+ LLXMLNodePtr root;
+ BOOL success = LLUICtrlFactory::getLayeredXMLNode(xml_filename, root);
+
+ if (!success)
+ {
+ llerrs << "Couldn't load string table " << xml_filename << llendl;
+ return false;
+ }
+
+ return LLTrans::parseLanguageStrings(root);
+}
diff --git a/indra/llui/lltransutil.h b/indra/llui/lltransutil.h
new file mode 100644
index 0000000000..2ddfd81361
--- /dev/null
+++ b/indra/llui/lltransutil.h
@@ -0,0 +1,51 @@
+/**
+ * @file lltransutil.h
+ * @brief LLTrans helper
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TRANSUTIL_H
+#define LL_TRANSUTIL_H
+
+#include "lltrans.h"
+
+namespace LLTransUtil
+{
+ /**
+ * @brief Parses the xml file that holds the strings. Used once on startup
+ * @param xml_filename Filename to parse
+ * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
+ * @returns true if the file was parsed successfully, true if something went wrong
+ */
+ bool parseStrings(const std::string& xml_filename, const std::set<std::string>& default_args);
+
+ bool parseLanguageStrings(const std::string& xml_filename);
+};
+
+#endif
diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp
index a4c9728402..2bbede8c13 100644
--- a/indra/llui/lluictrlfactory.cpp
+++ b/indra/llui/lluictrlfactory.cpp
@@ -71,14 +71,6 @@
#include "llui.h"
#include "llviewborder.h"
-const char XML_HEADER[] = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n";
-
-const S32 HPAD = 4;
-const S32 VPAD = 4;
-const S32 FLOATER_H_MARGIN = 15;
-const S32 MIN_WIDGET_HEIGHT = 10;
-const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;
-
LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION("Widget Construction");
LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS("Widget InitFromParams");
LLFastTimer::DeclareTimer FTM_WIDGET_SETUP("Widget Setup");
@@ -436,929 +428,3 @@ void LLUICtrlFactory::popFactoryFunctions()
mFactoryStack.pop_back();
}
}
-
-//
-// LLXSDWriter
-//
-LLXSDWriter::LLXSDWriter()
-{
- registerInspectFunc<bool>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4));
- registerInspectFunc<std::string>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
- registerInspectFunc<U8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4));
- registerInspectFunc<S8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4));
- registerInspectFunc<U16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4));
- registerInspectFunc<S16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4));
- registerInspectFunc<U32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4));
- registerInspectFunc<S32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4));
- registerInspectFunc<F32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4));
- registerInspectFunc<F64>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4));
- registerInspectFunc<LLColor4>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
- registerInspectFunc<LLUIColor>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
- registerInspectFunc<LLUUID>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
- registerInspectFunc<LLSD>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
-}
-
-void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
-{
- mSchemaNode = node;
- node->setName("xs:schema");
- node->createChild("attributeFormDefault", true)->setStringValue("unqualified");
- node->createChild("elementFormDefault", true)->setStringValue("qualified");
- node->createChild("targetNamespace", true)->setStringValue(xml_namespace);
- node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema");
- node->createChild("xmlns", true)->setStringValue(xml_namespace);
-
- node = node->createChild("xs:complexType", false);
- node->createChild("name", true)->setStringValue(type_name);
- node->createChild("mixed", true)->setStringValue("true");
-
- mAttributeNode = node;
- mElementNode = node->createChild("xs:choice", false);
- mElementNode->createChild("minOccurs", true)->setStringValue("0");
- mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded");
- block.inspectBlock(*this);
-
- // duplicate element choices
- LLXMLNodeList children;
- mElementNode->getChildren("xs:element", children, FALSE);
- for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it)
- {
- LLXMLNodePtr child_copy = child_it->second->deepCopy();
- std::string child_name;
- child_copy->getAttributeString("name", child_name);
- child_copy->setAttributeString("name", type_name + "." + child_name);
- mElementNode->addChild(child_copy);
- }
-
- LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false);
- element_declaration_node->createChild("name", true)->setStringValue(type_name);
- element_declaration_node->createChild("type", true)->setStringValue(type_name);
-}
-
-void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
-{
- name_stack_t non_empty_names;
- std::string attribute_name;
- for (name_stack_t::const_iterator it = stack.begin();
- it != stack.end();
- ++it)
- {
- const std::string& name = it->first;
- if (!name.empty())
- {
- non_empty_names.push_back(*it);
- }
- }
-
- for (name_stack_t::const_iterator it = non_empty_names.begin();
- it != non_empty_names.end();
- ++it)
- {
- if (!attribute_name.empty())
- {
- attribute_name += ".";
- }
- attribute_name += it->first;
- }
-
- // only flag non-nested attributes as mandatory, nested attributes have variant syntax
- // that can't be properly constrained in XSD
- // e.g. <foo mandatory.value="bar"/> vs <foo><mandatory value="bar"/></foo>
- bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1;
-
- // don't bother supporting "Multiple" params as xml attributes
- if (max_count <= 1)
- {
- // add compound attribute to root node
- addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values);
- }
-
- // now generated nested elements for compound attributes
- if (non_empty_names.size() > 1 && !attribute_mandatory)
- {
- std::string element_name;
-
- // traverse all but last element, leaving that as an attribute name
- name_stack_t::const_iterator end_it = non_empty_names.end();
- end_it--;
-
- for (name_stack_t::const_iterator it = non_empty_names.begin();
- it != end_it;
- ++it)
- {
- if (it != non_empty_names.begin())
- {
- element_name += ".";
- }
- element_name += it->first;
- }
-
- std::string short_attribute_name = non_empty_names.back().first;
-
- LLXMLNodePtr complex_type_node;
-
- // find existing element node here, starting at tail of child list
- if (mElementNode->mChildren.notNull())
- {
- for(LLXMLNodePtr element = mElementNode->mChildren->tail;
- element.notNull();
- element = element->mPrev)
- {
- std::string name;
- if(element->getAttributeString("name", name) && name == element_name)
- {
- complex_type_node = element->mChildren->head;
- break;
- }
- }
- }
- //create complex_type node
- //
- //<xs:element
- // maxOccurs="1"
- // minOccurs="0"
- // name="name">
- // <xs:complexType>
- // </xs:complexType>
- //</xs:element>
- if(complex_type_node.isNull())
- {
- complex_type_node = mElementNode->createChild("xs:element", false);
-
- complex_type_node->createChild("minOccurs", true)->setIntValue(min_count);
- complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count);
- complex_type_node->createChild("name", true)->setStringValue(element_name);
- complex_type_node = complex_type_node->createChild("xs:complexType", false);
- }
-
- addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values);
- }
-}
-
-void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values)
-{
- if (!attribute_name.empty())
- {
- LLXMLNodePtr new_enum_type_node;
- if (possible_values != NULL)
- {
- // custom attribute type, for example
- //<xs:simpleType>
- // <xs:restriction
- // base="xs:string">
- // <xs:enumeration
- // value="a" />
- // <xs:enumeration
- // value="b" />
- // </xs:restriction>
- // </xs:simpleType>
- new_enum_type_node = new LLXMLNode("xs:simpleType", false);
-
- LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false);
- restriction_node->createChild("base", true)->setStringValue("xs:string");
-
- for (std::vector<std::string>::const_iterator it = possible_values->begin();
- it != possible_values->end();
- ++it)
- {
- LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false);
- enum_node->createChild("value", true)->setStringValue(*it);
- }
- }
-
- string_set_t& attributes_written = mAttributesWritten[type_declaration_node];
-
- string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name);
-
- // attribute not yet declared
- if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it))
- {
- attributes_written.insert(found_it, attribute_name);
-
- LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false);
-
- // attribute name
- attribute_node->createChild("name", true)->setStringValue(attribute_name);
-
- if (new_enum_type_node.notNull())
- {
- attribute_node->addChild(new_enum_type_node);
- }
- else
- {
- // simple attribute type
- attribute_node->createChild("type", true)->setStringValue(type);
- }
-
- // required or optional
- attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional");
- }
- // attribute exists...handle collision of same name attributes with potentially different types
- else
- {
- LLXMLNodePtr attribute_declaration;
- if (type_declaration_node.notNull())
- {
- for(LLXMLNodePtr node = type_declaration_node->mChildren->tail;
- node.notNull();
- node = node->mPrev)
- {
- std::string name;
- if (node->getAttributeString("name", name) && name == attribute_name)
- {
- attribute_declaration = node;
- break;
- }
- }
- }
-
- bool new_type_is_enum = new_enum_type_node.notNull();
- bool existing_type_is_enum = !attribute_declaration->hasAttribute("type");
-
- // either type is enum, revert to string in collision
- // don't bother to check for enum equivalence
- if (new_type_is_enum || existing_type_is_enum)
- {
- if (attribute_declaration->hasAttribute("type"))
- {
- attribute_declaration->setAttributeString("type", "xs:string");
- }
- else
- {
- attribute_declaration->createChild("type", true)->setStringValue("xs:string");
- }
- attribute_declaration->deleteChildren("xs:simpleType");
- }
- else
- {
- // check for collision of different standard types
- std::string existing_type;
- attribute_declaration->getAttributeString("type", existing_type);
- // if current type is not the same as the new type, revert to strnig
- if (existing_type != type)
- {
- // ...than use most general type, string
- attribute_declaration->setAttributeString("type", "string");
- }
- }
- }
- }
-}
-
-//
-// LLXUIXSDWriter
-//
-void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block)
-{
- std::string file_name(path);
- file_name += type_name + ".xsd";
- LLXMLNodePtr root_nodep = new LLXMLNode();
-
- LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui");
-
- // add includes for all possible children
- const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
- const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
-
- // add include declarations for all valid children
- for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
- it != widget_registryp->currentRegistrar().endItems();
- ++it)
- {
- std::string widget_name = it->first;
- if (widget_name == type_name)
- {
- continue;
- }
- LLXMLNodePtr nodep = new LLXMLNode("xs:include", false);
- nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
-
- // add to front of schema
- mSchemaNode->addChild(nodep, mSchemaNode);
- }
-
- // add choices for valid children
- if (widget_registryp)
- {
- for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
- it != widget_registryp->currentRegistrar().endItems();
- ++it)
- {
- std::string widget_name = it->first;
- //<xs:element name="widget_name" type="widget_name">
- LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false);
- widget_node->createChild("name", true)->setStringValue(widget_name);
- widget_node->createChild("type", true)->setStringValue(widget_name);
- }
- }
-
- LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w");
- LLXMLNode::writeHeaderToFile(xsd_file);
- root_nodep->writeToFile(xsd_file);
- fclose(xsd_file);
-}
-
-//
-// LLXUIParser
-//
-LLXUIParser::LLXUIParser()
-: mLastWriteGeneration(-1),
- mCurReadDepth(0)
-{
- registerParserFuncs<bool>(boost::bind(&LLXUIParser::readBoolValue, this, _1),
- boost::bind(&LLXUIParser::writeBoolValue, this, _1, _2));
- registerParserFuncs<std::string>(boost::bind(&LLXUIParser::readStringValue, this, _1),
- boost::bind(&LLXUIParser::writeStringValue, this, _1, _2));
- registerParserFuncs<U8>(boost::bind(&LLXUIParser::readU8Value, this, _1),
- boost::bind(&LLXUIParser::writeU8Value, this, _1, _2));
- registerParserFuncs<S8>(boost::bind(&LLXUIParser::readS8Value, this, _1),
- boost::bind(&LLXUIParser::writeS8Value, this, _1, _2));
- registerParserFuncs<U16>(boost::bind(&LLXUIParser::readU16Value, this, _1),
- boost::bind(&LLXUIParser::writeU16Value, this, _1, _2));
- registerParserFuncs<S16>(boost::bind(&LLXUIParser::readS16Value, this, _1),
- boost::bind(&LLXUIParser::writeS16Value, this, _1, _2));
- registerParserFuncs<U32>(boost::bind(&LLXUIParser::readU32Value, this, _1),
- boost::bind(&LLXUIParser::writeU32Value, this, _1, _2));
- registerParserFuncs<S32>(boost::bind(&LLXUIParser::readS32Value, this, _1),
- boost::bind(&LLXUIParser::writeS32Value, this, _1, _2));
- registerParserFuncs<F32>(boost::bind(&LLXUIParser::readF32Value, this, _1),
- boost::bind(&LLXUIParser::writeF32Value, this, _1, _2));
- registerParserFuncs<F64>(boost::bind(&LLXUIParser::readF64Value, this, _1),
- boost::bind(&LLXUIParser::writeF64Value, this, _1, _2));
- registerParserFuncs<LLColor4>(boost::bind(&LLXUIParser::readColor4Value, this, _1),
- boost::bind(&LLXUIParser::writeColor4Value, this, _1, _2));
- registerParserFuncs<LLUIColor>(boost::bind(&LLXUIParser::readUIColorValue, this, _1),
- boost::bind(&LLXUIParser::writeUIColorValue, this, _1, _2));
- registerParserFuncs<LLUUID>(boost::bind(&LLXUIParser::readUUIDValue, this, _1),
- boost::bind(&LLXUIParser::writeUUIDValue, this, _1, _2));
- registerParserFuncs<LLSD>(boost::bind(&LLXUIParser::readSDValue, this, _1),
- boost::bind(&LLXUIParser::writeSDValue, this, _1, _2));
-}
-
-static LLFastTimer::DeclareTimer PARSE_XUI("XUI Parsing");
-
-void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent)
-{
- LLFastTimer timer(PARSE_XUI);
- mNameStack.clear();
- mCurReadDepth = 0;
- setParseSilently(silent);
-
- if (node.isNull())
- {
- parserWarning("Invalid node");
- }
- else
- {
- readXUIImpl(node, std::string(node->getName()->mString), block);
- }
-}
-
-bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, const std::string& scope, LLInitParam::BaseBlock& block)
-{
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(".");
-
- bool values_parsed = false;
-
- // submit attributes for current node
- values_parsed |= readAttributes(nodep, block);
-
- // treat text contents of xml node as "value" parameter
- std::string text_contents = nodep->getSanitizedValue();
- if (!text_contents.empty())
- {
- mCurReadNode = nodep;
- mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
- // child nodes are not necessarily valid parameters (could be a child widget)
- // so don't complain once we've recursed
- bool silent = mCurReadDepth > 0;
- if (!block.submitValue(mNameStack, *this, true))
- {
- mNameStack.pop_back();
- block.submitValue(mNameStack, *this, silent);
- }
- else
- {
- mNameStack.pop_back();
- }
- }
-
- // then traverse children
- // child node must start with last name of parent node (our "scope")
- // for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>"
- // which equates to the following nesting:
- // button
- // param
- // nested_param1
- // nested_param2
- // nested_param3
- mCurReadDepth++;
- for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();)
- {
- std::string child_name(childp->getName()->mString);
- S32 num_tokens_pushed = 0;
-
- // for non "dotted" child nodes check to see if child node maps to another widget type
- // and if not, treat as a child element of the current node
- // e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect"
- // since there is no widget named "rect"
- if (child_name.find(".") == std::string::npos)
- {
- mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
- num_tokens_pushed++;
- }
- else
- {
- // parse out "dotted" name into individual tokens
- tokenizer name_tokens(child_name, sep);
-
- tokenizer::iterator name_token_it = name_tokens.begin();
- if(name_token_it == name_tokens.end())
- {
- childp = childp->getNextSibling();
- continue;
- }
-
- // check for proper nesting
- if(!scope.empty() && *name_token_it != scope)
- {
- childp = childp->getNextSibling();
- continue;
- }
-
- // now ignore first token
- ++name_token_it;
-
- // copy remaining tokens on to our running token list
- for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
- {
- mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
- num_tokens_pushed++;
- }
- }
-
- // recurse and visit children XML nodes
- if(readXUIImpl(childp, mNameStack.empty() ? scope : mNameStack.back().first, block))
- {
- // child node successfully parsed, remove from DOM
-
- values_parsed = true;
- LLXMLNodePtr node_to_remove = childp;
- childp = childp->getNextSibling();
-
- nodep->deleteChild(node_to_remove);
- }
- else
- {
- childp = childp->getNextSibling();
- }
-
- while(num_tokens_pushed-- > 0)
- {
- mNameStack.pop_back();
- }
- }
- mCurReadDepth--;
- return values_parsed;
-}
-
-bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
-{
- typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
- boost::char_separator<char> sep(".");
-
- bool any_parsed = false;
-
- for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin();
- attribute_it != nodep->mAttributes.end();
- ++attribute_it)
- {
- S32 num_tokens_pushed = 0;
- std::string attribute_name(attribute_it->first->mString);
- mCurReadNode = attribute_it->second;
-
- tokenizer name_tokens(attribute_name, sep);
- // copy remaining tokens on to our running token list
- for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
- {
- mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
- num_tokens_pushed++;
- }
-
- // child nodes are not necessarily valid attributes, so don't complain once we've recursed
- bool silent = mCurReadDepth > 0;
- any_parsed |= block.submitValue(mNameStack, *this, silent);
-
- while(num_tokens_pushed-- > 0)
- {
- mNameStack.pop_back();
- }
- }
-
- return any_parsed;
-}
-
-void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block)
-{
- mWriteRootNode = node;
- block.serializeBlock(*this, Parser::name_stack_t(), diff_block);
- mOutNodes.clear();
-}
-
-// go from a stack of names to a specific XML node
-LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
-{
- name_stack_t name_stack;
- for (name_stack_t::const_iterator it = stack.begin();
- it != stack.end();
- ++it)
- {
- if (!it->first.empty())
- {
- name_stack.push_back(*it);
- }
- }
-
- LLXMLNodePtr out_node = mWriteRootNode;
-
- name_stack_t::const_iterator next_it = name_stack.begin();
- for (name_stack_t::const_iterator it = name_stack.begin();
- it != name_stack.end();
- it = next_it)
- {
- ++next_it;
- if (it->first.empty())
- {
- continue;
- }
-
- out_nodes_t::iterator found_it = mOutNodes.lower_bound(it->second);
-
- // node with this name not yet written
- if (found_it == mOutNodes.end() || mOutNodes.key_comp()(found_it->first, it->second))
- {
- // make an attribute if we are the last element on the name stack
- bool is_attribute = next_it == name_stack.end();
- LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
- out_node->addChild(new_node);
- mOutNodes.insert(found_it, std::make_pair(it->second, new_node));
- out_node = new_node;
- }
- else
- {
- out_node = found_it->second;
- }
- }
-
- return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
-}
-
-
-bool LLXUIParser::readBoolValue(void* val_ptr)
-{
- S32 value;
- bool success = mCurReadNode->getBoolValue(1, &value);
- *((bool*)val_ptr) = (value != FALSE);
- return success;
-}
-
-bool LLXUIParser::writeBoolValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setBoolValue(*((bool*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readStringValue(void* val_ptr)
-{
- *((std::string*)val_ptr) = mCurReadNode->getSanitizedValue();
- return true;
-}
-
-bool LLXUIParser::writeStringValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- const std::string* string_val = reinterpret_cast<const std::string*>(val_ptr);
- if (string_val->find('\n') != std::string::npos
- || string_val->size() > MAX_STRING_ATTRIBUTE_SIZE)
- {
- // don't write strings with newlines into attributes
- std::string attribute_name = node->getName()->mString;
- LLXMLNodePtr parent_node = node->mParent;
- parent_node->deleteChild(node);
- // write results in text contents of node
- if (attribute_name == "value")
- {
- // "value" is implicit, just write to parent
- node = parent_node;
- }
- else
- {
- // create a child that is not an attribute, but with same name
- node = parent_node->createChild(attribute_name.c_str(), false);
- }
- }
- node->setStringValue(*string_val);
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readU8Value(void* val_ptr)
-{
- return mCurReadNode->getByteValue(1, (U8*)val_ptr);
-}
-
-bool LLXUIParser::writeU8Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setUnsignedValue(*((U8*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readS8Value(void* val_ptr)
-{
- S32 value;
- if(mCurReadNode->getIntValue(1, &value))
- {
- *((S8*)val_ptr) = value;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeS8Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setIntValue(*((S8*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readU16Value(void* val_ptr)
-{
- U32 value;
- if(mCurReadNode->getUnsignedValue(1, &value))
- {
- *((U16*)val_ptr) = value;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeU16Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setUnsignedValue(*((U16*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readS16Value(void* val_ptr)
-{
- S32 value;
- if(mCurReadNode->getIntValue(1, &value))
- {
- *((S16*)val_ptr) = value;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeS16Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setIntValue(*((S16*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readU32Value(void* val_ptr)
-{
- return mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
-}
-
-bool LLXUIParser::writeU32Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setUnsignedValue(*((U32*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readS32Value(void* val_ptr)
-{
- return mCurReadNode->getIntValue(1, (S32*)val_ptr);
-}
-
-bool LLXUIParser::writeS32Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setIntValue(*((S32*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readF32Value(void* val_ptr)
-{
- return mCurReadNode->getFloatValue(1, (F32*)val_ptr);
-}
-
-bool LLXUIParser::writeF32Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setFloatValue(*((F32*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readF64Value(void* val_ptr)
-{
- return mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
-}
-
-bool LLXUIParser::writeF64Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setDoubleValue(*((F64*)val_ptr));
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readColor4Value(void* val_ptr)
-{
- LLColor4* colorp = (LLColor4*)val_ptr;
- if(mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
- {
- return true;
- }
-
- return false;
-}
-
-bool LLXUIParser::writeColor4Value(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- LLColor4 color = *((LLColor4*)val_ptr);
- node->setFloatValue(4, color.mV);
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readUIColorValue(void* val_ptr)
-{
- LLUIColor* param = (LLUIColor*)val_ptr;
- LLColor4 color;
- bool success = mCurReadNode->getFloatValue(4, color.mV) >= 3;
- if (success)
- {
- param->set(color);
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeUIColorValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- LLUIColor color = *((LLUIColor*)val_ptr);
- //RN: don't write out the color that is represented by a function
- // rely on param block exporting to get the reference to the color settings
- if (color.isReference()) return false;
- node->setFloatValue(4, color.get().mV);
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readUUIDValue(void* val_ptr)
-{
- LLUUID temp_id;
- // LLUUID::set is destructive, so use temporary value
- if (temp_id.set(mCurReadNode->getSanitizedValue()))
- {
- *(LLUUID*)(val_ptr) = temp_id;
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::writeUUIDValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- node->setStringValue(((LLUUID*)val_ptr)->asString());
- return true;
- }
- return false;
-}
-
-bool LLXUIParser::readSDValue(void* val_ptr)
-{
- *((LLSD*)val_ptr) = LLSD(mCurReadNode->getSanitizedValue());
- return true;
-}
-
-bool LLXUIParser::writeSDValue(const void* val_ptr, const name_stack_t& stack)
-{
- LLXMLNodePtr node = getNode(stack);
- if (node.notNull())
- {
- std::string string_val = ((LLSD*)val_ptr)->asString();
- if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE)
- {
- // don't write strings with newlines into attributes
- std::string attribute_name = node->getName()->mString;
- LLXMLNodePtr parent_node = node->mParent;
- parent_node->deleteChild(node);
- // write results in text contents of node
- if (attribute_name == "value")
- {
- // "value" is implicit, just write to parent
- node = parent_node;
- }
- else
- {
- node = parent_node->createChild(attribute_name.c_str(), false);
- }
- }
-
- node->setStringValue(string_val);
- return true;
- }
- return false;
-}
-
-/*virtual*/ std::string LLXUIParser::getCurrentElementName()
-{
- std::string full_name;
- for (name_stack_t::iterator it = mNameStack.begin();
- it != mNameStack.end();
- ++it)
- {
- full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
- }
-
- return full_name;
-}
-
-void LLXUIParser::parserWarning(const std::string& message)
-{
-#ifdef LL_WINDOWS
- // use Visual Studo friendly formatting of output message for easy access to originating xml
- llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
- utf16str += '\n';
- OutputDebugString(utf16str.c_str());
-#else
- Parser::parserWarning(message);
-#endif
-}
-
-void LLXUIParser::parserError(const std::string& message)
-{
-#ifdef LL_WINDOWS
- llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
- utf16str += '\n';
- OutputDebugString(utf16str.c_str());
-#else
- Parser::parserError(message);
-#endif
-}
diff --git a/indra/llui/lluictrlfactory.h b/indra/llui/lluictrlfactory.h
index b82feb3f58..e47010c316 100644
--- a/indra/llui/lluictrlfactory.h
+++ b/indra/llui/lluictrlfactory.h
@@ -35,9 +35,12 @@
#include "llcallbackmap.h"
#include "llinitparam.h"
+#include "llregistry.h"
#include "llxmlnode.h"
#include "llfasttimer.h"
+#include "llxuiparser.h"
+
#include <boost/function.hpp>
#include <iosfwd>
#include <stack>
@@ -47,110 +50,6 @@ class LLPanel;
class LLFloater;
class LLView;
-class LLXSDWriter : public LLInitParam::Parser
-{
- LOG_CLASS(LLXSDWriter);
-public:
- void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
-
- /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
-
- LLXSDWriter();
-
-protected:
- void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
- void addAttributeToSchema(LLXMLNodePtr nodep, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values);
- LLXMLNodePtr mAttributeNode;
- LLXMLNodePtr mElementNode;
- LLXMLNodePtr mSchemaNode;
-
- typedef std::set<std::string> string_set_t;
- typedef std::map<LLXMLNodePtr, string_set_t> attributes_map_t;
- attributes_map_t mAttributesWritten;
-};
-
-// NOTE: DOES NOT WORK YET
-// should support child widgets for XUI
-class LLXUIXSDWriter : public LLXSDWriter
-{
-public:
- void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
-};
-
-class LLXUIParser : public LLInitParam::Parser, public LLSingleton<LLXUIParser>
-{
-LOG_CLASS(LLXUIParser);
-
-protected:
- LLXUIParser();
- friend class LLSingleton<LLXUIParser>;
-public:
- typedef LLInitParam::Parser::name_stack_t name_stack_t;
-
- /*virtual*/ std::string getCurrentElementName();
- /*virtual*/ void parserWarning(const std::string& message);
- /*virtual*/ void parserError(const std::string& message);
-
- void readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent=false);
- void writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const LLInitParam::BaseBlock* diff_block = NULL);
-
-private:
- typedef std::list<std::pair<std::string, bool> > token_list_t;
-
- bool readXUIImpl(LLXMLNodePtr node, const std::string& scope, LLInitParam::BaseBlock& block);
- bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
-
- //reader helper functions
- bool readBoolValue(void* val_ptr);
- bool readStringValue(void* val_ptr);
- bool readU8Value(void* val_ptr);
- bool readS8Value(void* val_ptr);
- bool readU16Value(void* val_ptr);
- bool readS16Value(void* val_ptr);
- bool readU32Value(void* val_ptr);
- bool readS32Value(void* val_ptr);
- bool readF32Value(void* val_ptr);
- bool readF64Value(void* val_ptr);
- bool readColor4Value(void* val_ptr);
- bool readUIColorValue(void* val_ptr);
- bool readUUIDValue(void* val_ptr);
- bool readSDValue(void* val_ptr);
-
- //writer helper functions
- bool writeBoolValue(const void* val_ptr, const name_stack_t&);
- bool writeStringValue(const void* val_ptr, const name_stack_t&);
- bool writeU8Value(const void* val_ptr, const name_stack_t&);
- bool writeS8Value(const void* val_ptr, const name_stack_t&);
- bool writeU16Value(const void* val_ptr, const name_stack_t&);
- bool writeS16Value(const void* val_ptr, const name_stack_t&);
- bool writeU32Value(const void* val_ptr, const name_stack_t&);
- bool writeS32Value(const void* val_ptr, const name_stack_t&);
- bool writeF32Value(const void* val_ptr, const name_stack_t&);
- bool writeF64Value(const void* val_ptr, const name_stack_t&);
- bool writeColor4Value(const void* val_ptr, const name_stack_t&);
- bool writeUIColorValue(const void* val_ptr, const name_stack_t&);
- bool writeUUIDValue(const void* val_ptr, const name_stack_t&);
- bool writeSDValue(const void* val_ptr, const name_stack_t&);
-
- LLXMLNodePtr getNode(const name_stack_t& stack);
-
-private:
- Parser::name_stack_t mNameStack;
- LLXMLNodePtr mCurReadNode;
- // Root of the widget XML sub-tree, for example, "line_editor"
- LLXMLNodePtr mWriteRootNode;
-
- typedef std::map<S32, LLXMLNodePtr> out_nodes_t;
- out_nodes_t mOutNodes;
- S32 mLastWriteGeneration;
- LLXMLNodePtr mLastWrittenChild;
- S32 mCurReadDepth;
-};
-
-// global static instance for registering all widget types
-typedef boost::function<LLView* (LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)> LLWidgetCreatorFunc;
-
-typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
// sort functor for typeid maps
struct LLCompareTypeID
@@ -192,11 +91,6 @@ class LLWidgetNameRegistry
: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry , LLCompareTypeID>
{};
-// lookup widget type by name
-class LLWidgetTypeRegistry
-: public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
-{};
-
// lookup factory functions for default widget instances by widget type
typedef LLView* (*dummy_widget_creator_func_t)(const std::string&);
class LLDefaultWidgetRegistry
@@ -209,10 +103,6 @@ class LLDefaultParamBlockRegistry
: public LLRegistrySingleton<const std::type_info*, empty_param_block_func_t, LLDefaultParamBlockRegistry, LLCompareTypeID>
{};
-class LLChildRegistryRegistry
-: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
-{};
-
extern LLFastTimer::DeclareTimer FTM_WIDGET_SETUP;
extern LLFastTimer::DeclareTimer FTM_WIDGET_CONSTRUCTION;
extern LLFastTimer::DeclareTimer FTM_INIT_FROM_PARAMS;
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index bf40353410..ecda880c1f 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -2362,8 +2362,10 @@ void LLWindowSDL::spawnWebBrowser(const std::string& escaped_url)
# endif // LL_X11
std::string cmd, arg;
- cmd = gDirUtilp->getAppRODataDir().c_str();
- cmd += gDirUtilp->getDirDelimiter().c_str();
+ cmd = gDirUtilp->getAppRODataDir();
+ cmd += gDirUtilp->getDirDelimiter();
+ cmd += "etc";
+ cmd += gDirUtilp->getDirDelimiter();
cmd += "launch_url.sh";
arg = escaped_url;
exec_cmd(cmd, arg);
diff --git a/indra/llxuixml/CMakeLists.txt b/indra/llxuixml/CMakeLists.txt
new file mode 100644
index 0000000000..daed4de6ce
--- /dev/null
+++ b/indra/llxuixml/CMakeLists.txt
@@ -0,0 +1,45 @@
+# -*- cmake -*-
+
+project(llxuixml)
+
+include(00-Common)
+include(LLCommon)
+include(LLMath)
+include(LLXML)
+
+include_directories(
+ ${LLCOMMON_INCLUDE_DIRS}
+ ${LLMATH_INCLUDE_DIRS}
+ ${LLXML_INCLUDE_DIRS}
+ )
+
+set(llxuixml_SOURCE_FILES
+ llinitparam.cpp
+ lltrans.cpp
+ lluicolor.cpp
+ llxuiparser.cpp
+ )
+
+set(llxuixml_HEADER_FILES
+ CMakeLists.txt
+
+ llinitparam.h
+ lltrans.h
+ llregistry.h
+ lluicolor.h
+ llxuiparser.h
+ )
+
+set_source_files_properties(${llxuixml_HEADER_FILES}
+ PROPERTIES HEADER_FILE_ONLY TRUE)
+
+list(APPEND llxuixml_SOURCE_FILES ${llxuixml_HEADER_FILES})
+
+add_library (llxuixml ${llxuixml_SOURCE_FILES})
+# Libraries on which this library depends, needed for Linux builds
+# Sort by high-level to low-level
+target_link_libraries(llxuixml
+ llxml
+ llcommon
+ llmath
+ )
diff --git a/indra/llxuixml/llinitparam.cpp b/indra/llxuixml/llinitparam.cpp
new file mode 100644
index 0000000000..d35e7b40f8
--- /dev/null
+++ b/indra/llxuixml/llinitparam.cpp
@@ -0,0 +1,524 @@
+/**
+ * @file llinitparam.cpp
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ *
+ * Copyright (c) 2008-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llinitparam.h"
+
+
+namespace LLInitParam
+{
+ BlockDescriptor BaseBlock::sBlockDescriptor;
+
+ //
+ // Param
+ //
+ Param::Param(BaseBlock* enclosing_block)
+ : mIsProvided(false)
+ {
+ const U8* my_addr = reinterpret_cast<const U8*>(this);
+ const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block);
+ mEnclosingBlockOffset = (S16)(block_addr - my_addr);
+ }
+
+ //
+ // Parser
+ //
+ Parser::~Parser()
+ {}
+
+ void Parser::parserWarning(const std::string& message)
+ {
+ if (mParseSilently) return;
+ llwarns << message << llendl;
+ }
+
+ void Parser::parserError(const std::string& message)
+ {
+ if (mParseSilently) return;
+ llerrs << message << llendl;
+ }
+
+
+ //
+ // BlockDescriptor
+ //
+ void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data)
+ {
+ mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end());
+ mSynonyms.insert(src_block_data.mSynonyms.begin(), src_block_data.mSynonyms.end());
+ std::copy(src_block_data.mUnnamedParams.begin(), src_block_data.mUnnamedParams.end(), std::back_inserter(mUnnamedParams));
+ std::copy(src_block_data.mValidationList.begin(), src_block_data.mValidationList.end(), std::back_inserter(mValidationList));
+ std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams));
+ }
+
+ //
+ // BaseBlock
+ //
+ BaseBlock::BaseBlock()
+ : mLastChangedParam(0),
+ mChangeVersion(0)
+ {}
+
+ BaseBlock::~BaseBlock()
+ {}
+
+ // called by each derived class in least to most derived order
+ void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size)
+ {
+ mBlockDescriptor = &descriptor;
+
+ descriptor.mCurrentBlockPtr = this;
+ descriptor.mMaxParamOffset = block_size;
+
+ switch(descriptor.mInitializationState)
+ {
+ case BlockDescriptor::UNINITIALIZED:
+ // copy params from base class here
+ descriptor.aggregateBlockData(base_descriptor);
+
+ descriptor.mInitializationState = BlockDescriptor::INITIALIZING;
+ break;
+ case BlockDescriptor::INITIALIZING:
+ descriptor.mInitializationState = BlockDescriptor::INITIALIZED;
+ break;
+ case BlockDescriptor::INITIALIZED:
+ // nothing to do
+ break;
+ }
+ }
+
+ param_handle_t BaseBlock::getHandleFromParam(const Param* param) const
+ {
+ const U8* param_address = reinterpret_cast<const U8*>(param);
+ const U8* baseblock_address = reinterpret_cast<const U8*>(this);
+ return (param_address - baseblock_address);
+ }
+
+ bool BaseBlock::submitValue(const Parser::name_stack_t& name_stack, Parser& p, bool silent)
+ {
+ if (!deserializeBlock(p, boost::make_iterator_range(name_stack.begin(), name_stack.end())))
+ {
+ if (!silent)
+ {
+ p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str()));
+ }
+ return false;
+ }
+ return true;
+ }
+
+
+ bool BaseBlock::validateBlock(bool silent) const
+ {
+ const BlockDescriptor& block_data = getBlockDescriptor();
+ for (BlockDescriptor::param_validation_list_t::const_iterator it = block_data.mValidationList.begin(); it != block_data.mValidationList.end(); ++it)
+ {
+ const Param* param = getParamFromHandle(it->first);
+ if (!it->second(param))
+ {
+ if (!silent)
+ {
+ llwarns << "Invalid param \"" << getParamName(block_data, param) << "\"" << llendl;
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t name_stack, const LLInitParam::BaseBlock* diff_block) const
+ {
+ // named param is one like LLView::Params::follows
+ // unnamed param is like LLView::Params::rect - implicit
+ const BlockDescriptor& block_data = getBlockDescriptor();
+
+ for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin();
+ it != block_data.mUnnamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = (*it)->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::serialize_func_t serialize_func = (*it)->mSerializeFunc;
+ if (serialize_func)
+ {
+ const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+ // each param descriptor remembers its serial number
+ // so we can inspect the same param under different names
+ // and see that it has the same number
+ (*it)->mGeneration = parser.newParseGeneration();
+ name_stack.push_back(std::make_pair("", (*it)->mGeneration));
+ serialize_func(*param, parser, name_stack, diff_param);
+ name_stack.pop_back();
+ }
+ }
+
+ for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
+ it != block_data.mNamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = it->second->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::serialize_func_t serialize_func = it->second->mSerializeFunc;
+ if (serialize_func)
+ {
+ // Ensure this param has not already been serialized
+ // Prevents <rect> from being serialized as its own tag.
+ bool duplicate = false;
+ for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin();
+ it2 != block_data.mUnnamedParams.end();
+ ++it2)
+ {
+ if (param_handle == (*it2)->mParamHandle)
+ {
+ duplicate = true;
+ break;
+ }
+ }
+
+ //FIXME: for now, don't attempt to serialize values under synonyms, as current parsers
+ // don't know how to detect them
+ if (duplicate)
+ {
+ continue;
+ }
+
+ if (!duplicate)
+ {
+ it->second->mGeneration = parser.newParseGeneration();
+ }
+
+ name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+ const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL;
+ serialize_func(*param, parser, name_stack, diff_param);
+ name_stack.pop_back();
+ }
+ }
+
+ return true;
+ }
+
+ bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack) const
+ {
+ // named param is one like LLView::Params::follows
+ // unnamed param is like LLView::Params::rect - implicit
+ const BlockDescriptor& block_data = getBlockDescriptor();
+
+ for (BlockDescriptor::param_list_t::const_iterator it = block_data.mUnnamedParams.begin();
+ it != block_data.mUnnamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = (*it)->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::inspect_func_t inspect_func = (*it)->mInspectFunc;
+ if (inspect_func)
+ {
+ (*it)->mGeneration = parser.newParseGeneration();
+ name_stack.push_back(std::make_pair("", (*it)->mGeneration));
+ inspect_func(*param, parser, name_stack, (*it)->mMinCount, (*it)->mMaxCount);
+ name_stack.pop_back();
+ }
+ }
+
+ for(BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin();
+ it != block_data.mNamedParams.end();
+ ++it)
+ {
+ param_handle_t param_handle = it->second->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::inspect_func_t inspect_func = it->second->mInspectFunc;
+ if (inspect_func)
+ {
+ // Ensure this param has not already been inspected
+ bool duplicate = false;
+ for (BlockDescriptor::param_list_t::const_iterator it2 = block_data.mUnnamedParams.begin();
+ it2 != block_data.mUnnamedParams.end();
+ ++it2)
+ {
+ if (param_handle == (*it2)->mParamHandle)
+ {
+ duplicate = true;
+ break;
+ }
+ }
+
+ if (!duplicate)
+ {
+ it->second->mGeneration = parser.newParseGeneration();
+ }
+ name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+ inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount);
+ name_stack.pop_back();
+ }
+ }
+
+ for(BlockDescriptor::param_map_t::const_iterator it = block_data.mSynonyms.begin();
+ it != block_data.mSynonyms.end();
+ ++it)
+ {
+ param_handle_t param_handle = it->second->mParamHandle;
+ const Param* param = getParamFromHandle(param_handle);
+ ParamDescriptor::inspect_func_t inspect_func = it->second->mInspectFunc;
+ if (inspect_func)
+ {
+ // use existing serial number for param
+ name_stack.push_back(std::make_pair(it->first, it->second->mGeneration));
+ inspect_func(*param, parser, name_stack, it->second->mMinCount, it->second->mMaxCount);
+ name_stack.pop_back();
+ }
+ }
+
+ return true;
+ }
+
+ bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack)
+ {
+ BlockDescriptor& block_data = getBlockDescriptor();
+ bool names_left = !name_stack.empty();
+
+ if (names_left)
+ {
+ const std::string& top_name = name_stack.front().first;
+
+ ParamDescriptor::deserialize_func_t deserialize_func = NULL;
+ Param* paramp = NULL;
+
+ BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name);
+ if (found_it != block_data.mNamedParams.end())
+ {
+ // find pointer to member parameter from offset table
+ paramp = getParamFromHandle(found_it->second->mParamHandle);
+ deserialize_func = found_it->second->mDeserializeFunc;
+ }
+ else
+ {
+ BlockDescriptor::param_map_t::iterator found_it = block_data.mSynonyms.find(top_name);
+ if (found_it != block_data.mSynonyms.end())
+ {
+ // find pointer to member parameter from offset table
+ paramp = getParamFromHandle(found_it->second->mParamHandle);
+ deserialize_func = found_it->second->mDeserializeFunc;
+ }
+ }
+
+ Parser::name_stack_range_t new_name_stack(++name_stack.begin(), name_stack.end());
+ if (deserialize_func)
+ {
+ return deserialize_func(*paramp, p, new_name_stack, name_stack.empty() ? -1 : name_stack.front().second);
+ }
+ }
+
+ // try to parse unnamed parameters, in declaration order
+ for ( BlockDescriptor::param_list_t::iterator it = block_data.mUnnamedParams.begin();
+ it != block_data.mUnnamedParams.end();
+ ++it)
+ {
+ Param* paramp = getParamFromHandle((*it)->mParamHandle);
+ ParamDescriptor::deserialize_func_t deserialize_func = (*it)->mDeserializeFunc;
+
+ if (deserialize_func && deserialize_func(*paramp, p, name_stack, name_stack.empty() ? -1 : name_stack.front().second))
+ {
+ mLastChangedParam = (*it)->mParamHandle;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ //static
+ void BaseBlock::addParam(BlockDescriptor& block_data, const ParamDescriptor& in_param, const char* char_name)
+ {
+ // create a copy of the paramdescriptor in allparams
+ // so other data structures can store a pointer to it
+ block_data.mAllParams.push_back(in_param);
+ ParamDescriptor& param(block_data.mAllParams.back());
+
+ std::string name(char_name);
+ if ((size_t)param.mParamHandle > block_data.mMaxParamOffset)
+ {
+ llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl;
+ }
+
+ if (name.empty())
+ {
+ block_data.mUnnamedParams.push_back(&param);
+ }
+ else
+ {
+ // don't use insert, since we want to overwrite existing entries
+ block_data.mNamedParams[name] = &param;
+ }
+
+ if (param.mValidationFunc)
+ {
+ block_data.mValidationList.push_back(std::make_pair(param.mParamHandle, param.mValidationFunc));
+ }
+ }
+
+ void BaseBlock::addSynonym(Param& param, const std::string& synonym)
+ {
+ BlockDescriptor& block_data = getBlockDescriptor();
+ if (block_data.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ param_handle_t handle = getHandleFromParam(&param);
+
+ // check for invalid derivation from a paramblock (i.e. without using
+ // Block<T, Base_Class>
+ if ((size_t)handle > block_data.mMaxParamOffset)
+ {
+ llerrs << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << llendl;
+ }
+
+ ParamDescriptor* param_descriptor = findParamDescriptor(handle);
+ if (param_descriptor)
+ {
+ if (synonym.empty())
+ {
+ block_data.mUnnamedParams.push_back(param_descriptor);
+ }
+ else
+ {
+ block_data.mSynonyms[synonym] = param_descriptor;
+ }
+ }
+ }
+ }
+
+ void BaseBlock::setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ mLastChangedParam = getHandleFromParam(&last_param);
+ mChangeVersion++;
+ }
+
+ const std::string& BaseBlock::getParamName(const BlockDescriptor& block_data, const Param* paramp) const
+ {
+ param_handle_t handle = getHandleFromParam(paramp);
+ for (BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); it != block_data.mNamedParams.end(); ++it)
+ {
+ if (it->second->mParamHandle == handle)
+ {
+ return it->first;
+ }
+ }
+
+ for (BlockDescriptor::param_map_t::const_iterator it = block_data.mSynonyms.begin(); it != block_data.mSynonyms.end(); ++it)
+ {
+ if (it->second->mParamHandle == handle)
+ {
+ return it->first;
+ }
+ }
+
+ return LLStringUtil::null;
+ }
+
+ ParamDescriptor* BaseBlock::findParamDescriptor(param_handle_t handle)
+ {
+ BlockDescriptor& descriptor = getBlockDescriptor();
+ BlockDescriptor::all_params_list_t::iterator end_it = descriptor.mAllParams.end();
+ for (BlockDescriptor::all_params_list_t::iterator it = descriptor.mAllParams.begin();
+ it != end_it;
+ ++it)
+ {
+ if (it->mParamHandle == handle) return &(*it);
+ }
+ return NULL;
+ }
+
+ // take all provided params from other and apply to self
+ // NOTE: this requires that "other" is of the same derived type as this
+ bool BaseBlock::overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other)
+ {
+ bool param_changed = false;
+ BlockDescriptor::all_params_list_t::const_iterator end_it = block_data.mAllParams.end();
+ for (BlockDescriptor::all_params_list_t::const_iterator it = block_data.mAllParams.begin();
+ it != end_it;
+ ++it)
+ {
+ const Param* other_paramp = other.getParamFromHandle(it->mParamHandle);
+ ParamDescriptor::merge_func_t merge_func = it->mMergeFunc;
+ if (merge_func)
+ {
+ Param* paramp = getParamFromHandle(it->mParamHandle);
+ param_changed |= merge_func(*paramp, *other_paramp, true);
+ mLastChangedParam = it->mParamHandle;
+ }
+ }
+ return param_changed;
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool BaseBlock::fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other)
+ {
+ bool param_changed = false;
+ BlockDescriptor::all_params_list_t::const_iterator end_it = block_data.mAllParams.end();
+ for (BlockDescriptor::all_params_list_t::const_iterator it = block_data.mAllParams.begin();
+ it != end_it;
+ ++it)
+ {
+ const Param* other_paramp = other.getParamFromHandle(it->mParamHandle);
+ ParamDescriptor::merge_func_t merge_func = it->mMergeFunc;
+ if (merge_func)
+ {
+ Param* paramp = getParamFromHandle(it->mParamHandle);
+ param_changed |= merge_func(*paramp, *other_paramp, false);
+ mLastChangedParam = it->mParamHandle;
+ }
+ }
+ return param_changed;
+ }
+
+
+ template<>
+ bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals(
+ const boost::function<void (const std::string &,void *)> &a,
+ const boost::function<void (const std::string &,void *)> &b)
+ {
+ return false;
+ }
+
+ template<>
+ bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals(
+ const boost::function<void (const LLSD &,const LLSD &)> &a,
+ const boost::function<void (const LLSD &,const LLSD &)> &b)
+ {
+ return false;
+ }
+
+ template<>
+ bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b)
+ {
+ return false;
+ }
+}
diff --git a/indra/llxuixml/llinitparam.h b/indra/llxuixml/llinitparam.h
new file mode 100644
index 0000000000..cb56049ae2
--- /dev/null
+++ b/indra/llxuixml/llinitparam.h
@@ -0,0 +1,1822 @@
+/**
+ * @file llinitparam.h
+ * @brief parameter block abstraction for creating complex objects and
+ * parsing construction parameters from xml and LLSD
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ *
+ * Copyright (c) 2008-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLPARAM_H
+#define LL_LLPARAM_H
+
+#include <vector>
+
+#include <stddef.h>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <boost/range/iterator_range.hpp>
+#include "llregistry.h"
+#include "llmemory.h"
+
+
+namespace LLInitParam
+{
+ template <typename T>
+ class ParamCompare {
+ public:
+ static bool equals(const T &a, const T &b);
+ };
+
+ template<class T>
+ bool ParamCompare<T>::equals(const T &a, const T&b)
+ {
+ return a == b;
+ }
+
+
+ // default constructor adaptor for InitParam Values
+ // constructs default instances of the given type, returned by const reference
+ template <typename T>
+ struct DefaultInitializer
+ {
+ typedef const T& T_const_ref;
+ // return reference to a single default instance of T
+ // built-in types will be initialized to zero, default constructor otherwise
+ static T_const_ref get() { static T t = T(); return t; }
+ };
+
+ // helper functions and classes
+ typedef ptrdiff_t param_handle_t;
+
+ template <typename T>
+ class TypeValues
+ {
+ public:
+ // empty default implemenation of key cache
+ class KeyCache
+ {
+ public:
+ void setKey(const std::string& key) {}
+ std::string getKey() const { return ""; }
+ void clearKey(){}
+ };
+
+ static bool get(const std::string& name, T& value)
+ {
+ return false;
+ }
+
+ static bool empty()
+ {
+ return true;
+ }
+
+ static std::vector<std::string>* getPossibleValues() { return NULL; }
+ };
+
+ template <typename T, typename DERIVED_TYPE = TypeValues<T> >
+ class TypeValuesHelper
+ : public LLRegistrySingleton<std::string, T, DERIVED_TYPE >
+ {
+ typedef LLRegistrySingleton<std::string, T, DERIVED_TYPE> super_t;
+ typedef LLSingleton<DERIVED_TYPE> singleton_t;
+ public:
+
+ //TODO: cache key by index to save on param block size
+ class KeyCache
+ {
+ public:
+ void setKey(const std::string& key)
+ {
+ mKey = key;
+ }
+
+ void clearKey()
+ {
+ mKey = "";
+ }
+
+ std::string getKey() const
+ {
+ return mKey;
+ }
+
+ private:
+ std::string mKey;
+ };
+
+ static bool get(const std::string& name, T& value)
+ {
+ if (!singleton_t::instance().exists(name)) return false;
+
+ value = *singleton_t::instance().getValue(name);
+ return true;
+ }
+
+ static bool empty()
+ {
+ return singleton_t::instance().LLRegistry<std::string, T>::empty();
+ }
+
+ //override this to add name value pairs
+ static void declareValues() {}
+
+ void initSingleton()
+ {
+ DERIVED_TYPE::declareValues();
+ }
+
+ static const std::vector<std::string>* getPossibleValues()
+ {
+ // in order to return a pointer to a member, we lazily
+ // evaluate the result and store it in mValues here
+ if (singleton_t::instance().mValues.empty())
+ {
+ typename super_t::Registrar::registry_map_t::const_iterator it;
+ for (it = super_t::defaultRegistrar().beginItems(); it != super_t::defaultRegistrar().endItems(); ++it)
+ {
+ singleton_t::instance().mValues.push_back(it->first);
+ }
+ }
+ return &singleton_t::instance().mValues;
+ }
+
+
+ protected:
+ static void declare(const std::string& name, const T& value)
+ {
+ super_t::defaultRegistrar().add(name, value);
+ }
+
+ private:
+ std::vector<std::string> mValues;
+ };
+
+ class Parser
+ {
+ LOG_CLASS(Parser);
+
+ public:
+
+ struct CompareTypeID
+ {
+ bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
+ {
+ return lhs->before(*rhs);
+ }
+ };
+
+ typedef std::vector<std::pair<std::string, S32> > name_stack_t;
+ typedef boost::iterator_range<name_stack_t::const_iterator> name_stack_range_t;
+ typedef std::vector<std::string> possible_values_t;
+
+ typedef boost::function<bool (void*)> parser_read_func_t;
+ typedef boost::function<bool (const void*, const name_stack_t&)> parser_write_func_t;
+ typedef boost::function<void (const name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t;
+
+ typedef std::map<const std::type_info*, parser_read_func_t, CompareTypeID> parser_read_func_map_t;
+ typedef std::map<const std::type_info*, parser_write_func_t, CompareTypeID> parser_write_func_map_t;
+ typedef std::map<const std::type_info*, parser_inspect_func_t, CompareTypeID> parser_inspect_func_map_t;
+
+ Parser()
+ : mParseSilently(false),
+ mParseGeneration(0)
+ {}
+ virtual ~Parser();
+
+ template <typename T> bool readValue(T& param)
+ {
+ parser_read_func_map_t::iterator found_it = mParserReadFuncs.find(&typeid(T));
+ if (found_it != mParserReadFuncs.end())
+ {
+ return found_it->second((void*)&param);
+ }
+ return false;
+ }
+
+ template <typename T> bool writeValue(const T& param, const name_stack_t& name_stack)
+ {
+ parser_write_func_map_t::iterator found_it = mParserWriteFuncs.find(&typeid(T));
+ if (found_it != mParserWriteFuncs.end())
+ {
+ return found_it->second((const void*)&param, name_stack);
+ }
+ return false;
+ }
+
+ // dispatch inspection to registered inspection functions, for each parameter in a param block
+ template <typename T> bool inspectValue(const name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values)
+ {
+ parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs.find(&typeid(T));
+ if (found_it != mParserInspectFuncs.end())
+ {
+ found_it->second(name_stack, min_count, max_count, possible_values);
+ return true;
+ }
+ return false;
+ }
+
+ virtual std::string getCurrentElementName() = 0;
+ virtual void parserWarning(const std::string& message);
+ virtual void parserError(const std::string& message);
+ void setParseSilently(bool silent) { mParseSilently = silent; }
+ bool getParseSilently() { return mParseSilently; }
+
+ S32 getParseGeneration() { return mParseGeneration; }
+ S32 newParseGeneration() { return ++mParseGeneration; }
+
+
+ protected:
+ template <typename T>
+ void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func)
+ {
+ mParserReadFuncs.insert(std::make_pair(&typeid(T), read_func));
+ mParserWriteFuncs.insert(std::make_pair(&typeid(T), write_func));
+ }
+
+ template <typename T>
+ void registerInspectFunc(parser_inspect_func_t inspect_func)
+ {
+ mParserInspectFuncs.insert(std::make_pair(&typeid(T), inspect_func));
+ }
+
+ bool mParseSilently;
+
+ private:
+ parser_read_func_map_t mParserReadFuncs;
+ parser_write_func_map_t mParserWriteFuncs;
+ parser_inspect_func_map_t mParserInspectFuncs;
+ S32 mParseGeneration;
+ };
+
+ class BaseBlock;
+
+ class Param
+ {
+ public:
+ // public to allow choice blocks to clear provided flag on stale choices
+ void setProvided(bool is_provided) { mIsProvided = is_provided; }
+
+ protected:
+ bool getProvided() const { return mIsProvided; }
+
+ Param(class BaseBlock* enclosing_block);
+
+ // store pointer to enclosing block as offset to reduce space and allow for quick copying
+ BaseBlock& enclosingBlock() const
+ {
+ const U8* my_addr = reinterpret_cast<const U8*>(this);
+ // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class
+ return *const_cast<BaseBlock*>(
+ reinterpret_cast<const BaseBlock*>(my_addr + (ptrdiff_t)mEnclosingBlockOffset));
+ }
+
+ private:
+ friend class BaseBlock;
+
+ bool mIsProvided;
+ S16 mEnclosingBlockOffset;
+ };
+
+ // various callbacks and constraints associated with an individual param
+ struct ParamDescriptor
+ {
+ public:
+ typedef bool(*merge_func_t)(Param&, const Param&, bool);
+ typedef bool(*deserialize_func_t)(Param&, Parser&, const Parser::name_stack_range_t&, S32);
+ typedef void(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const Param* diff_param);
+ typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count);
+ typedef bool(*validation_func_t)(const Param*);
+
+ ParamDescriptor(param_handle_t p,
+ merge_func_t merge_func,
+ deserialize_func_t deserialize_func,
+ serialize_func_t serialize_func,
+ validation_func_t validation_func,
+ inspect_func_t inspect_func,
+ S32 min_count,
+ S32 max_count)
+ : mParamHandle(p),
+ mMergeFunc(merge_func),
+ mDeserializeFunc(deserialize_func),
+ mSerializeFunc(serialize_func),
+ mValidationFunc(validation_func),
+ mInspectFunc(inspect_func),
+ mMinCount(min_count),
+ mMaxCount(max_count),
+ mNumRefs(0)
+ {}
+
+ ParamDescriptor()
+ : mParamHandle(0),
+ mMergeFunc(NULL),
+ mDeserializeFunc(NULL),
+ mSerializeFunc(NULL),
+ mValidationFunc(NULL),
+ mInspectFunc(NULL),
+ mMinCount(0),
+ mMaxCount(0),
+ mGeneration(0),
+ mNumRefs(0)
+ {}
+
+ param_handle_t mParamHandle;
+
+ merge_func_t mMergeFunc;
+ deserialize_func_t mDeserializeFunc;
+ serialize_func_t mSerializeFunc;
+ inspect_func_t mInspectFunc;
+ validation_func_t mValidationFunc;
+ S32 mMinCount;
+ S32 mMaxCount;
+ S32 mGeneration;
+ S32 mNumRefs;
+ };
+
+ // each derived Block class keeps a static data structure maintaining offsets to various params
+ class BlockDescriptor
+ {
+ public:
+ BlockDescriptor()
+ : mMaxParamOffset(0),
+ mInitializationState(UNINITIALIZED)
+ {}
+
+ typedef enum e_initialization_state
+ {
+ UNINITIALIZED,
+ INITIALIZING,
+ INITIALIZED
+ } EInitializationState;
+
+ void aggregateBlockData(BlockDescriptor& src_block_data);
+
+ public:
+ typedef std::map<const std::string, ParamDescriptor*> param_map_t; // references param descriptors stored in mAllParams
+ typedef std::vector<ParamDescriptor*> param_list_t;
+
+ typedef std::list<ParamDescriptor> all_params_list_t;// references param descriptors stored in mAllParams
+ typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t;
+
+ param_map_t mNamedParams; // parameters with associated names
+ param_map_t mSynonyms; // parameters with alternate names
+ param_list_t mUnnamedParams; // parameters with_out_ associated names
+ param_validation_list_t mValidationList; // parameters that must be validated
+ all_params_list_t mAllParams; // all parameters, owns descriptors
+
+ size_t mMaxParamOffset;
+
+ EInitializationState mInitializationState; // whether or not static block data has been initialized
+ class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed
+ };
+
+ class BaseBlock
+ {
+ public:
+ // this typedef identifies derived classes as being blocks
+ typedef void baseblock_base_class_t;
+ LOG_CLASS(BaseBlock);
+ friend class Param;
+
+ BaseBlock();
+ virtual ~BaseBlock();
+ bool submitValue(const Parser::name_stack_t& name_stack, Parser& p, bool silent=false);
+
+ param_handle_t getHandleFromParam(const Param* param) const;
+ bool validateBlock(bool silent = false) const;
+
+ Param* getParamFromHandle(const param_handle_t param_handle)
+ {
+ if (param_handle == 0) return NULL;
+ U8* baseblock_address = reinterpret_cast<U8*>(this);
+ return reinterpret_cast<Param*>(baseblock_address + param_handle);
+ }
+
+ const Param* getParamFromHandle(const param_handle_t param_handle) const
+ {
+ const U8* baseblock_address = reinterpret_cast<const U8*>(this);
+ return reinterpret_cast<const Param*>(baseblock_address + param_handle);
+ }
+
+ void addSynonym(Param& param, const std::string& synonym);
+
+ // Blocks can override this to do custom tracking of changes
+ virtual void setLastChangedParam(const Param& last_param, bool user_provided);
+
+ const Param* getLastChangedParam() const { return mLastChangedParam ? getParamFromHandle(mLastChangedParam) : NULL; }
+ S32 getLastChangeVersion() const { return mChangeVersion; }
+
+ bool deserializeBlock(Parser& p, Parser::name_stack_range_t name_stack);
+ bool serializeBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), const BaseBlock* diff_block = NULL) const;
+ virtual bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t()) const;
+
+ const BlockDescriptor& getBlockDescriptor() const { return *mBlockDescriptor; }
+ BlockDescriptor& getBlockDescriptor() { return *mBlockDescriptor; }
+
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const BaseBlock& other)
+ {
+ return false;
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const BaseBlock& other)
+ {
+ return false;
+ }
+
+ static void addParam(BlockDescriptor& block_data, const ParamDescriptor& param, const char* name);
+ protected:
+ void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size);
+
+
+ // take all provided params from other and apply to self
+ bool overwriteFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFromImpl(BlockDescriptor& block_data, const BaseBlock& other);
+
+ // can be updated in getters
+ mutable param_handle_t mLastChangedParam;
+ mutable S32 mChangeVersion;
+
+ BlockDescriptor* mBlockDescriptor; // most derived block descriptor
+
+ static BlockDescriptor sBlockDescriptor;
+
+ private:
+ const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const;
+ ParamDescriptor* findParamDescriptor(param_handle_t handle);
+ };
+
+
+ template<typename T>
+ struct ParamIterator
+ {
+ typedef typename std::vector<T>::const_iterator const_iterator;
+ typedef typename std::vector<T>::iterator iterator;
+ };
+
+ // these templates allow us to distinguish between template parameters
+ // that derive from BaseBlock and those that don't
+ // this is supposedly faster than boost::is_convertible and the ilk
+ template<typename T, typename Void = void>
+ struct is_BaseBlock
+ : boost::false_type
+ {};
+
+ template<typename T>
+ struct is_BaseBlock<T, typename T::baseblock_base_class_t>
+ : boost::true_type
+ {};
+
+ // specialize for custom parsing/decomposition of specific classes
+ // e.g. TypedParam<LLRect> has left, top, right, bottom, etc...
+ template<typename T,
+ typename NAME_VALUE_LOOKUP = TypeValues<T>,
+ bool HAS_MULTIPLE_VALUES = false,
+ typename VALUE_IS_BLOCK = typename is_BaseBlock<T>::type>
+ class TypedParam
+ : public Param
+ {
+ public:
+ typedef const T& value_const_ref_t;
+ typedef value_const_ref_t value_assignment_t;
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr)
+ {
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+
+ mData.mValue = value;
+ }
+
+ bool isProvided() const { return Param::getProvided(); }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ // no further names in stack, attempt to parse value now
+ if (name_stack.empty())
+ {
+ if (parser.readValue<T>(typed_param.mData.mValue))
+ {
+ typed_param.setProvided(true);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ return true;
+ }
+
+ // try to parse a known named value
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, typed_param.mData.mValue))
+ {
+ typed_param.mData.setKey(name);
+ typed_param.setProvided(true);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ return true;
+ }
+
+ }
+ }
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!typed_param.isProvided()) return;
+
+ if (!name_stack.empty())
+ {
+ name_stack.back().second = parser.newParseGeneration();
+ }
+
+ std::string key = typed_param.mData.getKey();
+
+ // first try to write out name of name/value pair
+
+ if (!key.empty())
+ {
+ if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
+ {
+ if (!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ }
+ // then try to serialize value directly
+ else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), static_cast<const self_t*>(diff_param)->get())) {
+ if (!parser.writeValue<T>(typed_param.mData.mValue, name_stack))
+ {
+ return;
+ }
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // tell parser about our actual type
+ parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
+ // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4)
+ if (NAME_VALUE_LOOKUP::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
+ }
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ mData.mValue = val;
+ mData.clearKey();
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return mData.mValue;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+ if (src_typed_param.isProvided()
+ && (overwrite || !dst_typed_param.isProvided()))
+ {
+ dst_typed_param.mData.clearKey();
+ dst_typed_param = src_typed_param;
+ return true;
+ }
+ return false;
+ }
+
+ struct Data : public key_cache_t
+ {
+ T mValue;
+ };
+
+ Data mData;
+ };
+
+ // parameter that is a block
+ template <typename T, typename NAME_VALUE_LOOKUP>
+ class TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type>
+ : public T,
+ public Param
+ {
+ public:
+ typedef const T value_const_t;
+ typedef T value_t;
+ typedef value_const_t& value_const_ref_t;
+ typedef value_const_ref_t value_assignment_t;
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false, boost::true_type> self_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ T(value)
+ {
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ // attempt to parse block...
+ if(typed_param.deserializeBlock(parser, name_stack))
+ {
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ return true;
+ }
+
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, typed_param))
+ {
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.mData.setKey(name);
+ typed_param.mData.mKeyVersion = typed_param.getLastChangeVersion();
+ return true;
+ }
+
+ }
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!name_stack.empty())
+ {
+ name_stack.back().second = parser.newParseGeneration();
+ }
+
+ std::string key = typed_param.mData.getKey();
+ if (!key.empty() && typed_param.mData.mKeyVersion == typed_param.getLastChangeVersion())
+ {
+ if (!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ else
+ {
+ typed_param.serializeBlock(parser, name_stack, static_cast<const self_t*>(diff_param));
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // I am a param that is also a block, so just recurse into my contents
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ typed_param.inspectBlock(parser, name_stack);
+ }
+
+ // a param-that-is-a-block is provided when the user has set one of its child params
+ // *and* the block as a whole validates
+ bool isProvided() const
+ {
+ // only validate block when it hasn't already passed validation and user has supplied *some* value
+ if (Param::getProvided() && mData.mValidatedVersion < T::getLastChangeVersion())
+ {
+ // a sub-block is "provided" when it has been filled in enough to be valid
+ mData.mValidated = T::validateBlock(true);
+ mData.mValidatedVersion = T::getLastChangeVersion();
+ }
+ return Param::getProvided() && mData.mValidated;
+ }
+
+ // assign block contents to this param-that-is-a-block
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ value_t::operator=(val);
+ mData.clearKey();
+ // force revalidation of block by clearing known provided version
+ // next call to isProvided() will update provision status based on validity
+ mData.mValidatedVersion = 0;
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ // propagate changed status up to enclosing block
+ /*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ T::setLastChangedParam(last_param, user_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, user_provided);
+ if (user_provided)
+ {
+ // a child param has been explicitly changed
+ // so *some* aspect of this block is now provided
+ setProvided(true);
+ }
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return *this;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+ if (overwrite)
+ {
+ if (dst_typed_param.T::overwriteFrom(src_typed_param))
+ {
+ dst_typed_param.mData.clearKey();
+ return true;
+ }
+ }
+ else
+ {
+ if (dst_typed_param.T::fillFrom(src_typed_param))
+ {
+ dst_typed_param.mData.clearKey();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ struct Data : public key_cache_t
+ {
+ S32 mKeyVersion;
+ mutable S32 mValidatedVersion;
+ mutable bool mValidated; // lazy validation flag
+
+ Data()
+ : mKeyVersion(0),
+ mValidatedVersion(0),
+ mValidated(false)
+ {}
+ };
+ Data mData;
+ };
+
+ // container of non-block parameters
+ template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
+ class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type>
+ : public Param
+ {
+ public:
+ typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::false_type> self_t;
+ typedef typename std::vector<VALUE_TYPE> container_t;
+ typedef const container_t& value_assignment_t;
+
+ typedef VALUE_TYPE value_t;
+ typedef value_t& value_ref_t;
+ typedef const value_t& value_const_ref_t;
+
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mValues(value)
+ {
+ mCachedKeys.resize(mValues.size());
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ bool isProvided() const { return Param::getProvided(); }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ value_t value;
+ // no further names in stack, attempt to parse value now
+ if (name_stack.empty())
+ {
+ // attempt to read value directly
+ if (parser.readValue<value_t>(value))
+ {
+ typed_param.mValues.push_back(value);
+ // save an empty name/value key as a placeholder
+ typed_param.mCachedKeys.push_back(key_cache_t());
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ // try to parse a known named value
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, typed_param.mValues))
+ {
+ typed_param.mValues.push_back(value);
+ typed_param.mCachedKeys.push_back(key_cache_t());
+ typed_param.mCachedKeys.back().setKey(name);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ }
+ }
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!typed_param.isProvided() || name_stack.empty()) return;
+
+ typename container_t::const_iterator it = typed_param.mValues.begin();
+ for (typename std::vector<key_cache_t>::const_iterator key_it = typed_param.mCachedKeys.begin();
+ it != typed_param.mValues.end();
+ ++key_it, ++it)
+ {
+ std::string key = key_it->get();
+ name_stack.back().second = parser.newParseGeneration();
+
+ if(!key.empty())
+ {
+ if(!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ // not parse via name values, write out value directly
+ else if (!parser.writeValue<VALUE_TYPE>(*it, name_stack))
+ {
+ return;
+ }
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ parser.inspectValue<VALUE_TYPE>(name_stack, min_count, max_count, NULL);
+ if (NAME_VALUE_LOOKUP::getPossibleValues())
+ {
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, NAME_VALUE_LOOKUP::getPossibleValues());
+ }
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ mValues = val;
+ mCachedKeys.clear();
+ mCachedKeys.resize(mValues.size());
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ value_ref_t add()
+ {
+ mValues.push_back(value_t());
+ mCachedKeys.push_back(key_cache_t());
+ setProvided(true);
+ return mValues.back();
+ }
+
+ void add(value_const_ref_t item)
+ {
+ mValues.push_back(item);
+ mCachedKeys.push_back(key_cache_t());
+ setProvided(true);
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return self_t::get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ bool hasNValidElements(S32 n) const
+ {
+ return mValues.size() >= n;
+ }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return mValues;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_typed_param.isProvided()
+ && (overwrite || !isProvided()))
+ {
+ dst_typed_param = src_typed_param;
+ return true;
+ }
+ return false;
+ }
+
+ container_t mValues;
+ std::vector<key_cache_t> mCachedKeys;
+ };
+
+ // container of block parameters
+ template <typename VALUE_TYPE, typename NAME_VALUE_LOOKUP>
+ class TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type>
+ : public Param
+ {
+ public:
+ typedef TypedParam<VALUE_TYPE, NAME_VALUE_LOOKUP, true, boost::true_type> self_t;
+ typedef typename std::vector<VALUE_TYPE> container_t;
+ typedef const container_t& value_assignment_t;
+
+ typedef VALUE_TYPE value_t;
+ typedef value_t& value_ref_t;
+ typedef const value_t& value_const_ref_t;
+
+ typedef typename NAME_VALUE_LOOKUP::KeyCache key_cache_t;
+
+ TypedParam(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mValues(value),
+ mLastParamGeneration(0)
+ {
+ mCachedKeys.resize(mValues.size());
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ bool isProvided() const { return Param::getProvided(); }
+
+ value_ref_t operator[](S32 index) { return mValues[index]; }
+ value_const_ref_t operator[](S32 index) const { return mValues[index]; }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ if (generation != typed_param.mLastParamGeneration || typed_param.mValues.empty())
+ {
+ typed_param.mValues.push_back(value_t());
+ typed_param.mCachedKeys.push_back(Data());
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.mLastParamGeneration = generation;
+ }
+
+ value_t& value = typed_param.mValues.back();
+
+ // attempt to parse block...
+ if(value.deserializeBlock(parser, name_stack))
+ {
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ if(!NAME_VALUE_LOOKUP::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (NAME_VALUE_LOOKUP::get(name, value))
+ {
+ typed_param.mCachedKeys.back().setKey(name);
+ typed_param.mCachedKeys.back().mKeyVersion = value.getLastChangeVersion();
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+
+ }
+ }
+
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ if (!typed_param.isProvided() || name_stack.empty()) return;
+
+ typename container_t::const_iterator it = typed_param.mValues.begin();
+ for (typename std::vector<Data>::const_iterator key_it = typed_param.mCachedKeys.begin();
+ it != typed_param.mValues.end();
+ ++key_it, ++it)
+ {
+ name_stack.back().second = parser.newParseGeneration();
+
+ std::string key = key_it->getKey();
+ if (!key.empty() && key_it->mKeyVersion == it->getLastChangeVersion())
+ {
+ if(!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ // Not parsed via named values, write out value directly
+ // NOTE: currently we don't worry about removing default values in Multiple
+ else if (!it->serializeBlock(parser, name_stack, NULL))
+ {
+ return;
+ }
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // I am a vector of blocks, so describe my contents recursively
+ value_t().inspectBlock(parser, name_stack);
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ mValues = val;
+ mCachedKeys.clear();
+ mCachedKeys.resize(mValues.size());
+ setProvided(flag_as_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ value_ref_t add()
+ {
+ mValues.push_back(value_t());
+ mCachedKeys.push_back(Data());
+ setProvided(true);
+ return mValues.back();
+ }
+
+ void add(value_const_ref_t item)
+ {
+ mValues.push_back(item);
+ mCachedKeys.push_back(Data());
+ setProvided(true);
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return self_t::get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ U32 numValidElements() const
+ {
+ U32 count = 0;
+ for (typename container_t::const_iterator it = mValues.begin();
+ it != mValues.end();
+ ++it)
+ {
+ if(it->validateBlock(true)) count++;
+ }
+ return count;
+ }
+
+ protected:
+ value_assignment_t get() const
+ {
+ return mValues;
+ }
+
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_typed_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_typed_param.isProvided()
+ && (overwrite || !dst_typed_param.isProvided()))
+ {
+ dst_typed_param = src_typed_param;
+ return true;
+ }
+ return false;
+ }
+
+ struct Data : public key_cache_t
+ {
+ S32 mKeyVersion; // version of block for which key was last valid
+
+ Data() : mKeyVersion(0) {}
+ };
+
+ container_t mValues;
+ std::vector<Data> mCachedKeys;
+
+ S32 mLastParamGeneration;
+ };
+
+ template <typename DERIVED_BLOCK>
+ class Choice : public BaseBlock
+ {
+ typedef Choice<DERIVED_BLOCK> self_t;
+ typedef Choice<DERIVED_BLOCK> enclosing_block_t;
+
+ LOG_CLASS(self_t);
+ public:
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const self_t& other)
+ {
+ mCurChoice = other.mCurChoice;
+ return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const self_t& other)
+ {
+ return false;
+ }
+
+ // clear out old choice when param has changed
+ /*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ param_handle_t changed_param_handle = BaseBlock::getHandleFromParam(&last_param);
+ // if we have a new choice...
+ if (changed_param_handle != mCurChoice)
+ {
+ // clear provided flag on previous choice
+ Param* previous_choice = BaseBlock::getParamFromHandle(mCurChoice);
+ if (previous_choice)
+ {
+ previous_choice->setProvided(false);
+ }
+ mCurChoice = changed_param_handle;
+ }
+ BaseBlock::setLastChangedParam(last_param, user_provided);
+ }
+
+ protected:
+ Choice()
+ : mCurChoice(0)
+ {
+ BaseBlock::init(sBlockDescriptor, BaseBlock::sBlockDescriptor, sizeof(DERIVED_BLOCK));
+ }
+
+ // Alternatives are mutually exclusive wrt other Alternatives in the same block.
+ // One alternative in a block will always have isChosen() == true.
+ // At most one alternative in a block will have isProvided() == true.
+ template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ public:
+ friend class Choice<DERIVED_BLOCK>;
+
+ typedef Alternative<T, NAME_VALUE_LOOKUP> self_t;
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+
+ explicit Alternative(const char* name, value_assignment_t val = DefaultInitializer<T>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1),
+ mOriginalValue(val)
+ {
+ // assign initial choice to first declared option
+ DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr);
+ if (DERIVED_BLOCK::sBlockDescriptor.mInitializationState == BlockDescriptor::INITIALIZING
+ && blockp->mCurChoice == 0)
+ {
+ blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this);
+ }
+ }
+
+ Alternative& operator=(value_assignment_t val)
+ {
+ super_t::set(val);
+ return *this;
+ }
+
+ void operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ }
+
+ operator value_assignment_t() const
+ {
+ if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
+ {
+ return super_t::get();
+ }
+ return mOriginalValue;
+ }
+
+ value_assignment_t operator()() const
+ {
+ if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this)
+ {
+ return super_t::get();
+ }
+ return mOriginalValue;
+ }
+
+ bool isChosen() const
+ {
+ return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this;
+ }
+
+ private:
+ T mOriginalValue;
+ };
+
+ protected:
+ static BlockDescriptor sBlockDescriptor;
+
+ private:
+ param_handle_t mCurChoice;
+
+ const Param* getCurrentChoice() const
+ {
+ return BaseBlock::getParamFromHandle(mCurChoice);
+ }
+ };
+
+ template<typename DERIVED_BLOCK>
+ BlockDescriptor
+ Choice<DERIVED_BLOCK>::sBlockDescriptor;
+
+ //struct CardinalityConstraint
+ //{
+ // virtual std::pair<S32, S32> getRange() = 0;
+ //};
+
+ struct AnyAmount
+ {
+ static U32 minCount() { return 0; }
+ static U32 maxCount() { return U32_MAX; }
+ };
+
+ template<U32 MIN_AMOUNT>
+ struct AtLeast
+ {
+ static U32 minCount() { return MIN_AMOUNT; }
+ static U32 maxCount() { return U32_MAX; }
+ };
+
+ template<U32 MAX_AMOUNT>
+ struct AtMost
+ {
+ static U32 minCount() { return 0; }
+ static U32 maxCount() { return MAX_AMOUNT; }
+ };
+
+ template<U32 MIN_AMOUNT, U32 MAX_AMOUNT>
+ struct Between
+ {
+ static U32 minCount() { return MIN_AMOUNT; }
+ static U32 maxCount() { return MAX_AMOUNT; }
+ };
+
+ template<U32 EXACT_COUNT>
+ struct Exactly
+ {
+ static U32 minCount() { return EXACT_COUNT; }
+ static U32 maxCount() { return EXACT_COUNT; }
+ };
+
+ template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock>
+ class Block
+ : public BASE_BLOCK
+ {
+ typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t;
+ typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t;
+
+ public:
+ typedef BASE_BLOCK base_block_t;
+
+ // take all provided params from other and apply to self
+ bool overwriteFrom(const self_t& other)
+ {
+ return BaseBlock::overwriteFromImpl(sBlockDescriptor, other);
+ }
+
+ // take all provided params that are not already provided, and apply to self
+ bool fillFrom(const self_t& other)
+ {
+ return BaseBlock::fillFromImpl(sBlockDescriptor, other);
+ }
+ protected:
+ Block()
+ {
+ //#pragma message("Parsing LLInitParam::Block")
+ BaseBlock::init(sBlockDescriptor, BASE_BLOCK::sBlockDescriptor, sizeof(DERIVED_BLOCK));
+ }
+
+ //
+ // Nested classes for declaring parameters
+ //
+ template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ public:
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+
+ explicit Optional(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, NULL, 0, 1)
+ {
+ //#pragma message("Parsing LLInitParam::Block::Optional")
+ }
+
+ Optional& operator=(value_assignment_t val)
+ {
+ set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+ using super_t::operator();
+ };
+
+ template <typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false>
+ {
+ public:
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t;
+ typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+
+ // mandatory parameters require a name to be parseable
+ explicit Mandatory(const char* name = "", value_assignment_t val = DefaultInitializer<T>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, 1, 1)
+ {}
+
+ Mandatory& operator=(value_assignment_t val)
+ {
+ set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+ using super_t::operator();
+
+ static bool validate(const Param* p)
+ {
+ // valid only if provided
+ return static_cast<const self_t*>(p)->isProvided();
+ }
+
+ };
+
+ template <typename T, typename RANGE = AnyAmount, typename NAME_VALUE_LOOKUP = TypeValues<T> >
+ class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true>
+ {
+ public:
+ typedef TypedParam<T, NAME_VALUE_LOOKUP, true> super_t;
+ typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP> self_t;
+ typedef typename super_t::container_t container_t;
+ typedef typename super_t::value_assignment_t value_assignment_t;
+ typedef typename container_t::iterator iterator;
+ typedef typename container_t::const_iterator const_iterator;
+
+ explicit Multiple(const char* name = "", value_assignment_t val = DefaultInitializer<container_t>::get())
+ : super_t(DERIVED_BLOCK::sBlockDescriptor, name, val, &validate, RANGE::minCount(), RANGE::maxCount())
+ {}
+
+ using super_t::operator();
+
+ Multiple& operator=(value_assignment_t val)
+ {
+ set(val);
+ return *this;
+ }
+
+ DERIVED_BLOCK& operator()(typename super_t::value_assignment_t val)
+ {
+ super_t::set(val);
+ return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock());
+ }
+
+ static bool validate(const Param* paramp)
+ {
+ U32 num_valid = ((super_t*)paramp)->numValidElements();
+ return RANGE::minCount() <= num_valid && num_valid <= RANGE::maxCount();
+ }
+ };
+
+ class Deprecated : public Param
+ {
+ public:
+ explicit Deprecated(const char* name)
+ : Param(DERIVED_BLOCK::sBlockDescriptor.mCurrentBlockPtr)
+ {
+ BlockDescriptor& block_descriptor = DERIVED_BLOCK::sBlockDescriptor;
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ NULL,
+ &deserializeParam,
+ NULL,
+ NULL,
+ NULL,
+ 0, S32_MAX);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ if (name_stack.empty())
+ {
+ //std::string message = llformat("Deprecated value %s ignored", getName().c_str());
+ //parser.parserWarning(message);
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ typedef Deprecated Ignored;
+
+ protected:
+ static BlockDescriptor sBlockDescriptor;
+ };
+
+ template<typename DERIVED_BLOCK, typename BASE_BLOCK>
+ BlockDescriptor
+ Block<DERIVED_BLOCK, BASE_BLOCK>::sBlockDescriptor;
+
+ template<typename T, typename DERIVED = TypedParam<T> >
+ class BlockValue
+ : public Block<TypedParam<T, TypeValues<T>, false> >,
+ public Param
+ {
+ public:
+ typedef BlockValue<T> self_t;
+ typedef Block<TypedParam<T, TypeValues<T>, false> > block_t;
+ typedef const T& value_const_ref_t;
+ typedef value_const_ref_t value_assignment_t;
+ typedef typename TypeValues<T>::KeyCache key_cache_t;
+
+ BlockValue(BlockDescriptor& block_descriptor, const char* name, value_assignment_t value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count)
+ : Param(block_descriptor.mCurrentBlockPtr),
+ mData(value)
+ {
+ if (block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)
+ {
+ ParamDescriptor param_descriptor(block_descriptor.mCurrentBlockPtr->getHandleFromParam(this),
+ &mergeWith,
+ &deserializeParam,
+ &serializeParam,
+ validate_func,
+ &inspectParam,
+ min_count, max_count);
+ BaseBlock::addParam(block_descriptor, param_descriptor, name);
+ }
+ }
+
+ // implicit conversion
+ operator value_assignment_t() const { return get(); }
+ // explicit conversion
+ value_assignment_t operator()() const { return get(); }
+
+ static bool deserializeParam(Param& param, Parser& parser, const Parser::name_stack_range_t& name_stack, S32 generation)
+ {
+ self_t& typed_param = static_cast<self_t&>(param);
+ // type to apply parse direct value T
+ if (name_stack.empty())
+ {
+ if(parser.readValue<T>(typed_param.mData.mValue))
+ {
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
+ return true;
+ }
+
+ if(!TypeValues<T>::empty())
+ {
+ // try to parse a known named value
+ std::string name;
+ if (parser.readValue<std::string>(name))
+ {
+ // try to parse a per type named value
+ if (TypeValues<T>::get(name, typed_param.mData.mValue))
+ {
+ typed_param.mData.setKey(name);
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ typed_param.mData.mLastParamVersion = typed_param.BaseBlock::getLastChangeVersion();
+ return true;
+ }
+ }
+ }
+ }
+
+ // fall back on parsing block components for T
+ // if we deserialized at least one component...
+ if (typed_param.BaseBlock::deserializeBlock(parser, name_stack))
+ {
+ // ...our block is provided, and considered changed
+ typed_param.enclosingBlock().setLastChangedParam(param, true);
+ typed_param.setProvided(true);
+ return true;
+ }
+ return false;
+ }
+
+ static void serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const Param* diff_param)
+ {
+ const self_t& typed_param = static_cast<const self_t&>(param);
+
+ if (!typed_param.isProvided()) return;
+
+ std::string key = typed_param.mData.getKey();
+
+ // first try to write out name of name/value pair
+ if (!key.empty())
+ {
+ if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->mData.getKey(), key))
+ {
+ if (!parser.writeValue<std::string>(key, name_stack))
+ {
+ return;
+ }
+ }
+ }
+ // then try to serialize value directly
+ else if (!diff_param || !ParamCompare<T>::equals(typed_param.get(), (static_cast<const self_t*>(diff_param))->get()))
+ {
+
+ if (parser.writeValue<T>(typed_param.mData.mValue, name_stack))
+ {
+ return;
+ }
+
+ //RN: *always* serialize provided components of BlockValue (don't pass diff_param on),
+ // since these tend to be viewed as the constructor arguments for the value T. It seems
+ // cleaner to treat the uniqueness of a BlockValue according to the generated value, and
+ // not the individual components. This way <color red="0" green="1" blue="0"/> will not
+ // be exported as <color green="1"/>, since it was probably the intent of the user to
+ // be specific about the RGB color values. This also fixes an issue where we distinguish
+ // between rect.left not being provided and rect.left being explicitly set to 0 (same as default)
+ typed_param.BaseBlock::serializeBlock(parser, name_stack, NULL);
+ }
+ }
+
+ static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count)
+ {
+ // first, inspect with actual type...
+ parser.inspectValue<T>(name_stack, min_count, max_count, NULL);
+ if (TypeValues<T>::getPossibleValues())
+ {
+ //...then inspect with possible string values...
+ parser.inspectValue<std::string>(name_stack, min_count, max_count, TypeValues<T>::getPossibleValues());
+ }
+ // then recursively inspect contents...
+ const self_t& typed_param = static_cast<const self_t&>(param);
+ typed_param.inspectBlock(parser, name_stack);
+ }
+
+
+ bool isProvided() const
+ {
+ // either param value provided directly or block is sufficiently filled in
+ // if cached value is stale, regenerate from params
+ if (Param::getProvided() && mData.mLastParamVersion < BaseBlock::getLastChangeVersion())
+ {
+ if (block_t::validateBlock(true))
+ {
+ mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
+ // clear stale keyword associated with old value
+ mData.clearKey();
+ mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+ return true;
+ }
+ else
+ {
+ //block value incomplete, so not considered provided
+ // will attempt to revalidate on next call to isProvided()
+ return false;
+ }
+ }
+ // either no data provided, or we have a valid value in hand
+ return Param::getProvided();
+ }
+
+ void set(value_assignment_t val, bool flag_as_provided = true)
+ {
+ Param::enclosingBlock().setLastChangedParam(*this, flag_as_provided);
+ // set param version number to be up to date, so we ignore block contents
+ mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+
+ mData.mValue = val;
+ mData.clearKey();
+ setProvided(flag_as_provided);
+ }
+
+ void setIfNotProvided(value_assignment_t val, bool flag_as_provided = true)
+ {
+ // don't override any user provided value
+ if (!isProvided())
+ {
+ set(val, flag_as_provided);
+ }
+ }
+
+ // propagate change status up to enclosing block
+ /*virtual*/ void setLastChangedParam(const Param& last_param, bool user_provided)
+ {
+ BaseBlock::setLastChangedParam(last_param, user_provided);
+ Param::enclosingBlock().setLastChangedParam(*this, user_provided);
+ if (user_provided)
+ {
+ setProvided(true); // some component provided
+ }
+ }
+
+ protected:
+ value_assignment_t get() const
+ {
+ if (mData.mLastParamVersion < BaseBlock::getLastChangeVersion() && block_t::validateBlock(true))
+ {
+ mData.mValue = static_cast<const DERIVED*>(this)->getValueFromBlock();
+ mData.clearKey();
+ mData.mLastParamVersion = BaseBlock::getLastChangeVersion();
+ }
+
+ return mData.mValue;
+ }
+
+ // mutable to allow lazy updates on get
+ struct Data : public key_cache_t
+ {
+ Data(const T& value)
+ : mValue(value),
+ mLastParamVersion(0)
+ {}
+
+ T mValue;
+ S32 mLastParamVersion;
+ };
+
+ mutable Data mData;
+
+ private:
+ static bool mergeWith(Param& dst, const Param& src, bool overwrite)
+ {
+ const self_t& src_param = static_cast<const self_t&>(src);
+ self_t& dst_typed_param = static_cast<self_t&>(dst);
+
+ if (src_param.isProvided()
+ && (overwrite || !dst_typed_param.isProvided()))
+ {
+ // assign individual parameters
+ if (overwrite)
+ {
+ dst_typed_param.BaseBlock::overwriteFromImpl(block_t::sBlockDescriptor, src_param);
+ }
+ else
+ {
+ dst_typed_param.BaseBlock::fillFromImpl(block_t::sBlockDescriptor, src_param);
+ }
+ // then copy actual value
+ dst_typed_param.mData.mValue = src_param.get();
+ dst_typed_param.mData.clearKey();
+ dst_typed_param.setProvided(true);
+ return true;
+ }
+ return false;
+ }
+ };
+
+ template<>
+ bool ParamCompare<boost::function<void (const std::string &,void *)> >::equals(
+ const boost::function<void (const std::string &,void *)> &a,
+ const boost::function<void (const std::string &,void *)> &b);
+
+ template<>
+ bool ParamCompare<boost::function<void (const LLSD &,const LLSD &)> >::equals(
+ const boost::function<void (const LLSD &,const LLSD &)> &a,
+ const boost::function<void (const LLSD &,const LLSD &)> &b);
+
+ template<>
+ bool ParamCompare<LLSD>::equals(const LLSD &a, const LLSD &b);
+}
+
+#endif // LL_LLPARAM_H
diff --git a/indra/llxuixml/llregistry.h b/indra/llxuixml/llregistry.h
new file mode 100644
index 0000000000..2c04d8c419
--- /dev/null
+++ b/indra/llxuixml/llregistry.h
@@ -0,0 +1,347 @@
+/**
+ * @file llregistry.h
+ * @brief template classes for registering name, value pairs in nested scopes, statically, etc.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLREGISTRY_H
+#define LL_LLREGISTRY_H
+
+#include <list>
+
+#include <boost/type_traits.hpp>
+#include "llsingleton.h"
+
+template <typename T>
+class LLRegistryDefaultComparator
+{
+ bool operator()(const T& lhs, const T& rhs) { return lhs < rhs; }
+};
+
+template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistry
+{
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef typename boost::add_reference<typename boost::add_const<KEY>::type>::type ref_const_key_t;
+ typedef typename boost::add_reference<typename boost::add_const<VALUE>::type>::type ref_const_value_t;
+ typedef typename boost::add_reference<VALUE>::type ref_value_t;
+ typedef typename boost::add_pointer<typename boost::add_const<VALUE>::type>::type ptr_const_value_t;
+ typedef typename boost::add_pointer<VALUE>::type ptr_value_t;
+
+ class Registrar
+ {
+ friend class LLRegistry<KEY, VALUE, COMPARATOR>;
+ public:
+ typedef typename std::map<KEY, VALUE> registry_map_t;
+
+ bool add(ref_const_key_t key, ref_const_value_t value)
+ {
+ if (mMap.insert(std::make_pair(key, value)).second == false)
+ {
+ llwarns << "Tried to register " << key << " but it was already registered!" << llendl;
+ return false;
+ }
+ return true;
+ }
+
+ void remove(ref_const_key_t key)
+ {
+ mMap.erase(key);
+ }
+
+ typename registry_map_t::const_iterator beginItems() const
+ {
+ return mMap.begin();
+ }
+
+ typename registry_map_t::const_iterator endItems() const
+ {
+ return mMap.end();
+ }
+
+ protected:
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ typename registry_map_t::iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ typename registry_map_t::const_iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ // if the registry is used to store pointers, and null values are valid entries
+ // then use this function to check the existence of an entry
+ bool exists(ref_const_key_t key) const
+ {
+ return mMap.find(key) != mMap.end();
+ }
+
+ bool empty() const
+ {
+ return mMap.empty();
+ }
+
+ protected:
+ // use currentRegistrar() or defaultRegistrar()
+ Registrar() {}
+ ~Registrar() {}
+
+ private:
+ registry_map_t mMap;
+ };
+
+ typedef typename std::list<Registrar*> scope_list_t;
+ typedef typename std::list<Registrar*>::iterator scope_list_iterator_t;
+ typedef typename std::list<Registrar*>::const_iterator scope_list_const_iterator_t;
+
+ LLRegistry()
+ {}
+
+ ~LLRegistry() {}
+
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ for(scope_list_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ ptr_value_t valuep = (*it)->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ ptr_value_t valuep = (*it)->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ bool exists(ref_const_key_t key) const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ if ((*it)->exists(key)) return true;
+ }
+
+ return mDefaultRegistrar.exists(key);
+ }
+
+ bool empty() const
+ {
+ for(scope_list_const_iterator_t it = mActiveScopes.begin();
+ it != mActiveScopes.end();
+ ++it)
+ {
+ if (!(*it)->empty()) return false;
+ }
+
+ return mDefaultRegistrar.empty();
+ }
+
+
+ Registrar& defaultRegistrar()
+ {
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& defaultRegistrar() const
+ {
+ return mDefaultRegistrar;
+ }
+
+
+ Registrar& currentRegistrar()
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& currentRegistrar() const
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+
+protected:
+ void addScope(Registrar* scope)
+ {
+ // newer scopes go up front
+ mActiveScopes.insert(mActiveScopes.begin(), scope);
+ }
+
+ void removeScope(Registrar* scope)
+ {
+ // O(N) but should be near the beggining and N should be small and this is safer than storing iterators
+ scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope);
+ if (iter != mActiveScopes.end())
+ {
+ mActiveScopes.erase(iter);
+ }
+ }
+
+private:
+ scope_list_t mActiveScopes;
+ Registrar mDefaultRegistrar;
+};
+
+template <typename KEY, typename VALUE, typename DERIVED_TYPE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistrySingleton
+ : public LLRegistry<KEY, VALUE, COMPARATOR>,
+ public LLSingleton<DERIVED_TYPE>
+{
+ friend class LLSingleton<DERIVED_TYPE>;
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef const KEY& ref_const_key_t;
+ typedef const VALUE& ref_const_value_t;
+ typedef VALUE* ptr_value_t;
+ typedef const VALUE* ptr_const_value_t;
+ typedef LLSingleton<DERIVED_TYPE> singleton_t;
+
+ class ScopedRegistrar : public registry_t::Registrar
+ {
+ public:
+ ScopedRegistrar(bool push_scope = true)
+ {
+ if (push_scope)
+ {
+ pushScope();
+ }
+ }
+
+ ~ScopedRegistrar()
+ {
+ if (!singleton_t::destroyed())
+ {
+ popScope();
+ }
+ }
+
+ void pushScope()
+ {
+ singleton_t::instance().addScope(this);
+ }
+
+ void popScope()
+ {
+ singleton_t::instance().removeScope(this);
+ }
+
+ ptr_value_t getValueFromScope(ref_const_key_t key)
+ {
+ return getValue(key);
+ }
+
+ ptr_const_value_t getValueFromScope(ref_const_key_t key) const
+ {
+ return getValue(key);
+ }
+
+ private:
+ typename std::list<typename registry_t::Registrar*>::iterator mListIt;
+ };
+
+ class StaticRegistrar : public registry_t::Registrar
+ {
+ public:
+ virtual ~StaticRegistrar() {}
+ StaticRegistrar(ref_const_key_t key, ref_const_value_t value)
+ {
+ singleton_t::instance().mStaticScope->add(key, value);
+ }
+ };
+
+ // convenience functions
+ typedef typename LLRegistry<KEY, VALUE, COMPARATOR>::Registrar& ref_registrar_t;
+ static ref_registrar_t currentRegistrar()
+ {
+ return singleton_t::instance().registry_t::currentRegistrar();
+ }
+
+ static ref_registrar_t defaultRegistrar()
+ {
+ return singleton_t::instance().registry_t::defaultRegistrar();
+ }
+
+ static ptr_value_t getValue(ref_const_key_t key)
+ {
+ return singleton_t::instance().registry_t::getValue(key);
+ }
+
+protected:
+ // DERIVED_TYPE needs to derive from LLRegistrySingleton
+ LLRegistrySingleton()
+ : mStaticScope(NULL)
+ {}
+
+ virtual void initSingleton()
+ {
+ mStaticScope = new ScopedRegistrar();
+ }
+
+ virtual ~LLRegistrySingleton()
+ {
+ delete mStaticScope;
+ }
+
+private:
+ ScopedRegistrar* mStaticScope;
+};
+
+#endif
diff --git a/indra/llxuixml/lltrans.cpp b/indra/llxuixml/lltrans.cpp
new file mode 100644
index 0000000000..db7421575c
--- /dev/null
+++ b/indra/llxuixml/lltrans.cpp
@@ -0,0 +1,171 @@
+/**
+ * @file lltrans.cpp
+ * @brief LLTrans implementation
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltrans.h"
+
+#include "llfasttimer.h" // for call count statistics
+#include "llxuiparser.h"
+
+#include <map>
+
+LLTrans::template_map_t LLTrans::sStringTemplates;
+LLStringUtil::format_map_t LLTrans::sDefaultArgs;
+
+struct StringDef : public LLInitParam::Block<StringDef>
+{
+ Mandatory<std::string> name;
+ Mandatory<std::string> value;
+
+ StringDef()
+ : name("name"),
+ value("value")
+ {}
+};
+
+struct StringTable : public LLInitParam::Block<StringTable>
+{
+ Multiple<StringDef> strings;
+ StringTable()
+ : strings("string")
+ {}
+};
+
+//static
+bool LLTrans::parseStrings(LLXMLNodePtr &root, const std::set<std::string>& default_args)
+{
+ std::string xml_filename = "(strings file)";
+ if (!root->hasName("strings"))
+ {
+ llerrs << "Invalid root node name in " << xml_filename
+ << ": was " << root->getName() << ", expected \"strings\"" << llendl;
+ }
+
+ StringTable string_table;
+ LLXUIParser::instance().readXUI(root, string_table);
+
+ if (!string_table.validateBlock())
+ {
+ llerrs << "Problem reading strings: " << xml_filename << llendl;
+ return false;
+ }
+
+ sStringTemplates.clear();
+ sDefaultArgs.clear();
+
+ for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings().begin();
+ it != string_table.strings().end();
+ ++it)
+ {
+ LLTransTemplate xml_template(it->name, it->value);
+ sStringTemplates[xml_template.mName] = xml_template;
+
+ std::set<std::string>::const_iterator iter = default_args.find(xml_template.mName);
+ if (iter != default_args.end())
+ {
+ std::string name = *iter;
+ if (name[0] != '[')
+ name = llformat("[%s]",name.c_str());
+ sDefaultArgs[name] = xml_template.mText;
+ }
+ }
+
+ return true;
+}
+
+
+//static
+bool LLTrans::parseLanguageStrings(LLXMLNodePtr &root)
+{
+ std::string xml_filename = "(language strings file)";
+ if (!root->hasName("strings"))
+ {
+ llerrs << "Invalid root node name in " << xml_filename
+ << ": was " << root->getName() << ", expected \"strings\"" << llendl;
+ }
+
+ StringTable string_table;
+ LLXUIParser::instance().readXUI(root, string_table);
+
+ if (!string_table.validateBlock())
+ {
+ llerrs << "Problem reading strings: " << xml_filename << llendl;
+ return false;
+ }
+
+ for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings().begin();
+ it != string_table.strings().end();
+ ++it)
+ {
+ // share the same map with parseStrings() so we can search the strings using the same getString() function.- angela
+ LLTransTemplate xml_template(it->name, it->value);
+ sStringTemplates[xml_template.mName] = xml_template;
+ }
+
+ return true;
+}
+
+
+
+static LLFastTimer::DeclareTimer FTM_GET_TRANS("Translate string");
+
+//static
+std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
+{
+ // Don't care about time as much as call count. Make sure we're not
+ // calling LLTrans::getString() in an inner loop. JC
+ LLFastTimer timer(FTM_GET_TRANS);
+
+ template_map_t::iterator iter = sStringTemplates.find(xml_desc);
+ if (iter != sStringTemplates.end())
+ {
+ std::string text = iter->second.mText;
+ LLStringUtil::format_map_t args = sDefaultArgs;
+ args.insert(msg_args.begin(), msg_args.end());
+ LLStringUtil::format(text, args);
+
+ return text;
+ }
+ else
+ {
+ LLSD args;
+ args["STRING_NAME"] = xml_desc;
+ LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
+
+ //LLNotifications::instance().add("MissingString", args); // *TODO: resurrect
+ //return xml_desc;
+
+ return "MissingString("+xml_desc+")";
+ }
+}
+
diff --git a/indra/llxuixml/lltrans.h b/indra/llxuixml/lltrans.h
new file mode 100644
index 0000000000..6423c88245
--- /dev/null
+++ b/indra/llxuixml/lltrans.h
@@ -0,0 +1,111 @@
+/**
+ * @file lltrans.h
+ * @brief LLTrans definition
+ *
+ * $LicenseInfo:firstyear=2000&license=viewergpl$
+ *
+ * Copyright (c) 2000-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TRANS_H
+#define LL_TRANS_H
+
+#include <map>
+
+#include "llstring.h"
+#include "llxmlnode.h"
+
+/**
+ * @brief String template loaded from strings.xml
+ */
+class LLTransTemplate
+{
+public:
+ LLTransTemplate(const std::string& name = LLStringUtil::null, const std::string& text = LLStringUtil::null) : mName(name), mText(text) {}
+
+ std::string mName;
+ std::string mText;
+};
+
+/**
+ * @brief Localized strings class
+ * This class is used to retrieve translations of strings used to build larger ones, as well as
+ * strings with a general usage that don't belong to any specific floater. For example,
+ * "Owner:", "Retrieving..." used in the place of a not yet known name, etc.
+ */
+class LLTrans
+{
+public:
+ LLTrans();
+
+ /**
+ * @brief Parses the xml root that holds the strings. Used once on startup
+// *FIXME * @param xml_filename Filename to parse
+ * @param default_args Set of strings (expected to be in the file) to use as default replacement args, e.g. "SECOND_LIFE"
+ * @returns true if the file was parsed successfully, true if something went wrong
+ */
+ static bool parseStrings(LLXMLNodePtr& root, const std::set<std::string>& default_args);
+
+ static bool parseLanguageStrings(LLXMLNodePtr &root);
+
+ /**
+ * @brief Returns a translated string
+ * @param xml_desc String's description
+ * @param args A list of substrings to replace in the string
+ * @returns Translated string
+ */
+ static std::string getString(const std::string &xml_desc, const LLStringUtil::format_map_t& args);
+
+ /**
+ * @brief Returns a translated string
+ * @param xml_desc String's description
+ * @returns Translated string
+ */
+ static std::string getString(const std::string &xml_desc)
+ {
+ LLStringUtil::format_map_t empty;
+ return getString(xml_desc, empty);
+ }
+
+ // get the default args
+ static const LLStringUtil::format_map_t& getDefaultArgs()
+ {
+ return sDefaultArgs;
+ }
+
+ // insert default args into an arg list
+ static void getArgs(LLStringUtil::format_map_t& args)
+ {
+ args.insert(sDefaultArgs.begin(), sDefaultArgs.end());
+ }
+
+private:
+ typedef std::map<std::string, LLTransTemplate > template_map_t;
+ static template_map_t sStringTemplates;
+ static LLStringUtil::format_map_t sDefaultArgs;
+};
+
+#endif
diff --git a/indra/llxuixml/lluicolor.cpp b/indra/llxuixml/lluicolor.cpp
new file mode 100644
index 0000000000..ef0fa5d634
--- /dev/null
+++ b/indra/llxuixml/lluicolor.cpp
@@ -0,0 +1,71 @@
+/**
+ * @file lluicolor.cpp
+ * @brief brief LLUIColor class implementation file
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#include "lluicolor.h"
+
+LLUIColor::LLUIColor()
+ :mColorPtr(NULL)
+{
+}
+
+LLUIColor::LLUIColor(const LLColor4* color)
+ :mColorPtr(color)
+{
+}
+
+LLUIColor::LLUIColor(const LLColor4& color)
+ :mColor(color), mColorPtr(NULL)
+{
+}
+
+void LLUIColor::set(const LLColor4& color)
+{
+ mColor = color;
+ mColorPtr = NULL;
+}
+
+void LLUIColor::set(const LLColor4* color)
+{
+ mColorPtr = color;
+}
+
+const LLColor4& LLUIColor::get() const
+{
+ return (mColorPtr == NULL ? mColor : *mColorPtr);
+}
+
+LLUIColor::operator const LLColor4& () const
+{
+ return get();
+}
+
+const LLColor4& LLUIColor::operator()() const
+{
+ return get();
+}
+
+bool LLUIColor::isReference() const
+{
+ return mColorPtr != NULL;
+}
+
+namespace LLInitParam
+{
+ // used to detect equivalence with default values on export
+ template<>
+ class ParamCompare<LLUIColor>
+ {
+ public:
+ static bool equals(const LLUIColor &a, const LLUIColor &b)
+ {
+ // do not detect value equivalence, treat pointers to colors as distinct from color values
+ return (a.mColorPtr == NULL && b.mColorPtr == NULL ? a.mColor == b.mColor : a.mColorPtr == b.mColorPtr);
+ }
+ };
+}
diff --git a/indra/llxuixml/lluicolor.h b/indra/llxuixml/lluicolor.h
new file mode 100644
index 0000000000..365f61003b
--- /dev/null
+++ b/indra/llxuixml/lluicolor.h
@@ -0,0 +1,45 @@
+/**
+ * @file lluicolor.h
+ * @brief brief LLUIColor class header file
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLUICOLOR_H_
+#define LL_LLUICOLOR_H_
+
+#include "v4color.h"
+
+namespace LLInitParam
+{
+ template<typename T>
+ class ParamCompare;
+}
+
+class LLUIColor
+{
+public:
+ LLUIColor();
+ LLUIColor(const LLColor4* color);
+ LLUIColor(const LLColor4& color);
+
+ void set(const LLColor4& color);
+ void set(const LLColor4* color);
+
+ const LLColor4& get() const;
+
+ operator const LLColor4& () const;
+ const LLColor4& operator()() const;
+
+ bool isReference() const;
+
+private:
+ friend class LLInitParam::ParamCompare<LLUIColor>;
+
+ const LLColor4* mColorPtr;
+ LLColor4 mColor;
+};
+
+#endif
diff --git a/indra/llxuixml/llxuiparser.cpp b/indra/llxuixml/llxuiparser.cpp
new file mode 100644
index 0000000000..e1f61906e2
--- /dev/null
+++ b/indra/llxuixml/llxuiparser.cpp
@@ -0,0 +1,968 @@
+/**
+ * @file llxuiparser.cpp
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llxuiparser.h"
+
+#include <fstream>
+#include <boost/tokenizer.hpp>
+
+#include "lluicolor.h"
+
+const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;
+
+//
+// LLXSDWriter
+//
+LLXSDWriter::LLXSDWriter()
+{
+ registerInspectFunc<bool>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4));
+ registerInspectFunc<std::string>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<U8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4));
+ registerInspectFunc<S8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4));
+ registerInspectFunc<U16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4));
+ registerInspectFunc<S16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4));
+ registerInspectFunc<U32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4));
+ registerInspectFunc<S32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4));
+ registerInspectFunc<F32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4));
+ registerInspectFunc<F64>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4));
+ registerInspectFunc<LLColor4>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLUIColor>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLUUID>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+ registerInspectFunc<LLSD>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
+}
+
+void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
+{
+ mSchemaNode = node;
+ node->setName("xs:schema");
+ node->createChild("attributeFormDefault", true)->setStringValue("unqualified");
+ node->createChild("elementFormDefault", true)->setStringValue("qualified");
+ node->createChild("targetNamespace", true)->setStringValue(xml_namespace);
+ node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema");
+ node->createChild("xmlns", true)->setStringValue(xml_namespace);
+
+ node = node->createChild("xs:complexType", false);
+ node->createChild("name", true)->setStringValue(type_name);
+ node->createChild("mixed", true)->setStringValue("true");
+
+ mAttributeNode = node;
+ mElementNode = node->createChild("xs:choice", false);
+ mElementNode->createChild("minOccurs", true)->setStringValue("0");
+ mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded");
+ block.inspectBlock(*this);
+
+ // duplicate element choices
+ LLXMLNodeList children;
+ mElementNode->getChildren("xs:element", children, FALSE);
+ for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it)
+ {
+ LLXMLNodePtr child_copy = child_it->second->deepCopy();
+ std::string child_name;
+ child_copy->getAttributeString("name", child_name);
+ child_copy->setAttributeString("name", type_name + "." + child_name);
+ mElementNode->addChild(child_copy);
+ }
+
+ LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false);
+ element_declaration_node->createChild("name", true)->setStringValue(type_name);
+ element_declaration_node->createChild("type", true)->setStringValue(type_name);
+}
+
+void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
+{
+ name_stack_t non_empty_names;
+ std::string attribute_name;
+ for (name_stack_t::const_iterator it = stack.begin();
+ it != stack.end();
+ ++it)
+ {
+ const std::string& name = it->first;
+ if (!name.empty())
+ {
+ non_empty_names.push_back(*it);
+ }
+ }
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != non_empty_names.end();
+ ++it)
+ {
+ if (!attribute_name.empty())
+ {
+ attribute_name += ".";
+ }
+ attribute_name += it->first;
+ }
+
+ // only flag non-nested attributes as mandatory, nested attributes have variant syntax
+ // that can't be properly constrained in XSD
+ // e.g. <foo mandatory.value="bar"/> vs <foo><mandatory value="bar"/></foo>
+ bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1;
+
+ // don't bother supporting "Multiple" params as xml attributes
+ if (max_count <= 1)
+ {
+ // add compound attribute to root node
+ addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values);
+ }
+
+ // now generated nested elements for compound attributes
+ if (non_empty_names.size() > 1 && !attribute_mandatory)
+ {
+ std::string element_name;
+
+ // traverse all but last element, leaving that as an attribute name
+ name_stack_t::const_iterator end_it = non_empty_names.end();
+ end_it--;
+
+ for (name_stack_t::const_iterator it = non_empty_names.begin();
+ it != end_it;
+ ++it)
+ {
+ if (it != non_empty_names.begin())
+ {
+ element_name += ".";
+ }
+ element_name += it->first;
+ }
+
+ std::string short_attribute_name = non_empty_names.back().first;
+
+ LLXMLNodePtr complex_type_node;
+
+ // find existing element node here, starting at tail of child list
+ if (mElementNode->mChildren.notNull())
+ {
+ for(LLXMLNodePtr element = mElementNode->mChildren->tail;
+ element.notNull();
+ element = element->mPrev)
+ {
+ std::string name;
+ if(element->getAttributeString("name", name) && name == element_name)
+ {
+ complex_type_node = element->mChildren->head;
+ break;
+ }
+ }
+ }
+ //create complex_type node
+ //
+ //<xs:element
+ // maxOccurs="1"
+ // minOccurs="0"
+ // name="name">
+ // <xs:complexType>
+ // </xs:complexType>
+ //</xs:element>
+ if(complex_type_node.isNull())
+ {
+ complex_type_node = mElementNode->createChild("xs:element", false);
+
+ complex_type_node->createChild("minOccurs", true)->setIntValue(min_count);
+ complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count);
+ complex_type_node->createChild("name", true)->setStringValue(element_name);
+ complex_type_node = complex_type_node->createChild("xs:complexType", false);
+ }
+
+ addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values);
+ }
+}
+
+void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values)
+{
+ if (!attribute_name.empty())
+ {
+ LLXMLNodePtr new_enum_type_node;
+ if (possible_values != NULL)
+ {
+ // custom attribute type, for example
+ //<xs:simpleType>
+ // <xs:restriction
+ // base="xs:string">
+ // <xs:enumeration
+ // value="a" />
+ // <xs:enumeration
+ // value="b" />
+ // </xs:restriction>
+ // </xs:simpleType>
+ new_enum_type_node = new LLXMLNode("xs:simpleType", false);
+
+ LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false);
+ restriction_node->createChild("base", true)->setStringValue("xs:string");
+
+ for (std::vector<std::string>::const_iterator it = possible_values->begin();
+ it != possible_values->end();
+ ++it)
+ {
+ LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false);
+ enum_node->createChild("value", true)->setStringValue(*it);
+ }
+ }
+
+ string_set_t& attributes_written = mAttributesWritten[type_declaration_node];
+
+ string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name);
+
+ // attribute not yet declared
+ if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it))
+ {
+ attributes_written.insert(found_it, attribute_name);
+
+ LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false);
+
+ // attribute name
+ attribute_node->createChild("name", true)->setStringValue(attribute_name);
+
+ if (new_enum_type_node.notNull())
+ {
+ attribute_node->addChild(new_enum_type_node);
+ }
+ else
+ {
+ // simple attribute type
+ attribute_node->createChild("type", true)->setStringValue(type);
+ }
+
+ // required or optional
+ attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional");
+ }
+ // attribute exists...handle collision of same name attributes with potentially different types
+ else
+ {
+ LLXMLNodePtr attribute_declaration;
+ if (type_declaration_node.notNull())
+ {
+ for(LLXMLNodePtr node = type_declaration_node->mChildren->tail;
+ node.notNull();
+ node = node->mPrev)
+ {
+ std::string name;
+ if (node->getAttributeString("name", name) && name == attribute_name)
+ {
+ attribute_declaration = node;
+ break;
+ }
+ }
+ }
+
+ bool new_type_is_enum = new_enum_type_node.notNull();
+ bool existing_type_is_enum = !attribute_declaration->hasAttribute("type");
+
+ // either type is enum, revert to string in collision
+ // don't bother to check for enum equivalence
+ if (new_type_is_enum || existing_type_is_enum)
+ {
+ if (attribute_declaration->hasAttribute("type"))
+ {
+ attribute_declaration->setAttributeString("type", "xs:string");
+ }
+ else
+ {
+ attribute_declaration->createChild("type", true)->setStringValue("xs:string");
+ }
+ attribute_declaration->deleteChildren("xs:simpleType");
+ }
+ else
+ {
+ // check for collision of different standard types
+ std::string existing_type;
+ attribute_declaration->getAttributeString("type", existing_type);
+ // if current type is not the same as the new type, revert to strnig
+ if (existing_type != type)
+ {
+ // ...than use most general type, string
+ attribute_declaration->setAttributeString("type", "string");
+ }
+ }
+ }
+ }
+}
+
+//
+// LLXUIXSDWriter
+//
+void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block)
+{
+ std::string file_name(path);
+ file_name += type_name + ".xsd";
+ LLXMLNodePtr root_nodep = new LLXMLNode();
+
+ LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui");
+
+ // add includes for all possible children
+ const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
+ const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);
+
+ // add include declarations for all valid children
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string widget_name = it->first;
+ if (widget_name == type_name)
+ {
+ continue;
+ }
+ LLXMLNodePtr nodep = new LLXMLNode("xs:include", false);
+ nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
+
+ // add to front of schema
+ mSchemaNode->addChild(nodep, mSchemaNode);
+ }
+
+ // add choices for valid children
+ if (widget_registryp)
+ {
+ for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
+ it != widget_registryp->currentRegistrar().endItems();
+ ++it)
+ {
+ std::string widget_name = it->first;
+ //<xs:element name="widget_name" type="widget_name">
+ LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false);
+ widget_node->createChild("name", true)->setStringValue(widget_name);
+ widget_node->createChild("type", true)->setStringValue(widget_name);
+ }
+ }
+
+ LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w");
+ LLXMLNode::writeHeaderToFile(xsd_file);
+ root_nodep->writeToFile(xsd_file);
+ fclose(xsd_file);
+}
+
+//
+// LLXUIParser
+//
+LLXUIParser::LLXUIParser()
+: mLastWriteGeneration(-1),
+ mCurReadDepth(0)
+{
+ registerParserFuncs<bool>(boost::bind(&LLXUIParser::readBoolValue, this, _1),
+ boost::bind(&LLXUIParser::writeBoolValue, this, _1, _2));
+ registerParserFuncs<std::string>(boost::bind(&LLXUIParser::readStringValue, this, _1),
+ boost::bind(&LLXUIParser::writeStringValue, this, _1, _2));
+ registerParserFuncs<U8>(boost::bind(&LLXUIParser::readU8Value, this, _1),
+ boost::bind(&LLXUIParser::writeU8Value, this, _1, _2));
+ registerParserFuncs<S8>(boost::bind(&LLXUIParser::readS8Value, this, _1),
+ boost::bind(&LLXUIParser::writeS8Value, this, _1, _2));
+ registerParserFuncs<U16>(boost::bind(&LLXUIParser::readU16Value, this, _1),
+ boost::bind(&LLXUIParser::writeU16Value, this, _1, _2));
+ registerParserFuncs<S16>(boost::bind(&LLXUIParser::readS16Value, this, _1),
+ boost::bind(&LLXUIParser::writeS16Value, this, _1, _2));
+ registerParserFuncs<U32>(boost::bind(&LLXUIParser::readU32Value, this, _1),
+ boost::bind(&LLXUIParser::writeU32Value, this, _1, _2));
+ registerParserFuncs<S32>(boost::bind(&LLXUIParser::readS32Value, this, _1),
+ boost::bind(&LLXUIParser::writeS32Value, this, _1, _2));
+ registerParserFuncs<F32>(boost::bind(&LLXUIParser::readF32Value, this, _1),
+ boost::bind(&LLXUIParser::writeF32Value, this, _1, _2));
+ registerParserFuncs<F64>(boost::bind(&LLXUIParser::readF64Value, this, _1),
+ boost::bind(&LLXUIParser::writeF64Value, this, _1, _2));
+ registerParserFuncs<LLColor4>(boost::bind(&LLXUIParser::readColor4Value, this, _1),
+ boost::bind(&LLXUIParser::writeColor4Value, this, _1, _2));
+ registerParserFuncs<LLUIColor>(boost::bind(&LLXUIParser::readUIColorValue, this, _1),
+ boost::bind(&LLXUIParser::writeUIColorValue, this, _1, _2));
+ registerParserFuncs<LLUUID>(boost::bind(&LLXUIParser::readUUIDValue, this, _1),
+ boost::bind(&LLXUIParser::writeUUIDValue, this, _1, _2));
+ registerParserFuncs<LLSD>(boost::bind(&LLXUIParser::readSDValue, this, _1),
+ boost::bind(&LLXUIParser::writeSDValue, this, _1, _2));
+}
+
+static LLFastTimer::DeclareTimer PARSE_XUI("XUI Parsing");
+
+void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent)
+{
+ LLFastTimer timer(PARSE_XUI);
+ mNameStack.clear();
+ mCurReadDepth = 0;
+ setParseSilently(silent);
+
+ if (node.isNull())
+ {
+ parserWarning("Invalid node");
+ }
+ else
+ {
+ readXUIImpl(node, std::string(node->getName()->mString), block);
+ }
+}
+
+bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, const std::string& scope, LLInitParam::BaseBlock& block)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ bool values_parsed = false;
+
+ // submit attributes for current node
+ values_parsed |= readAttributes(nodep, block);
+
+ // treat text contents of xml node as "value" parameter
+ std::string text_contents = nodep->getSanitizedValue();
+ if (!text_contents.empty())
+ {
+ mCurReadNode = nodep;
+ mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
+ // child nodes are not necessarily valid parameters (could be a child widget)
+ // so don't complain once we've recursed
+ bool silent = mCurReadDepth > 0;
+ if (!block.submitValue(mNameStack, *this, true))
+ {
+ mNameStack.pop_back();
+ block.submitValue(mNameStack, *this, silent);
+ }
+ else
+ {
+ mNameStack.pop_back();
+ }
+ }
+
+ // then traverse children
+ // child node must start with last name of parent node (our "scope")
+ // for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>"
+ // which equates to the following nesting:
+ // button
+ // param
+ // nested_param1
+ // nested_param2
+ // nested_param3
+ mCurReadDepth++;
+ for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();)
+ {
+ std::string child_name(childp->getName()->mString);
+ S32 num_tokens_pushed = 0;
+
+ // for non "dotted" child nodes check to see if child node maps to another widget type
+ // and if not, treat as a child element of the current node
+ // e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect"
+ // since there is no widget named "rect"
+ if (child_name.find(".") == std::string::npos)
+ {
+ mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
+ num_tokens_pushed++;
+ }
+ else
+ {
+ // parse out "dotted" name into individual tokens
+ tokenizer name_tokens(child_name, sep);
+
+ tokenizer::iterator name_token_it = name_tokens.begin();
+ if(name_token_it == name_tokens.end())
+ {
+ childp = childp->getNextSibling();
+ continue;
+ }
+
+ // check for proper nesting
+ if(!scope.empty() && *name_token_it != scope)
+ {
+ childp = childp->getNextSibling();
+ continue;
+ }
+
+ // now ignore first token
+ ++name_token_it;
+
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
+ num_tokens_pushed++;
+ }
+ }
+
+ // recurse and visit children XML nodes
+ if(readXUIImpl(childp, mNameStack.empty() ? scope : mNameStack.back().first, block))
+ {
+ // child node successfully parsed, remove from DOM
+
+ values_parsed = true;
+ LLXMLNodePtr node_to_remove = childp;
+ childp = childp->getNextSibling();
+
+ nodep->deleteChild(node_to_remove);
+ }
+ else
+ {
+ childp = childp->getNextSibling();
+ }
+
+ while(num_tokens_pushed-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ }
+ mCurReadDepth--;
+ return values_parsed;
+}
+
+bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep(".");
+
+ bool any_parsed = false;
+
+ for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin();
+ attribute_it != nodep->mAttributes.end();
+ ++attribute_it)
+ {
+ S32 num_tokens_pushed = 0;
+ std::string attribute_name(attribute_it->first->mString);
+ mCurReadNode = attribute_it->second;
+
+ tokenizer name_tokens(attribute_name, sep);
+ // copy remaining tokens on to our running token list
+ for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
+ {
+ mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
+ num_tokens_pushed++;
+ }
+
+ // child nodes are not necessarily valid attributes, so don't complain once we've recursed
+ bool silent = mCurReadDepth > 0;
+ any_parsed |= block.submitValue(mNameStack, *this, silent);
+
+ while(num_tokens_pushed-- > 0)
+ {
+ mNameStack.pop_back();
+ }
+ }
+
+ return any_parsed;
+}
+
+void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block)
+{
+ mWriteRootNode = node;
+ block.serializeBlock(*this, Parser::name_stack_t(), diff_block);
+ mOutNodes.clear();
+}
+
+// go from a stack of names to a specific XML node
+LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
+{
+ name_stack_t name_stack;
+ for (name_stack_t::const_iterator it = stack.begin();
+ it != stack.end();
+ ++it)
+ {
+ if (!it->first.empty())
+ {
+ name_stack.push_back(*it);
+ }
+ }
+
+ LLXMLNodePtr out_node = mWriteRootNode;
+
+ name_stack_t::const_iterator next_it = name_stack.begin();
+ for (name_stack_t::const_iterator it = name_stack.begin();
+ it != name_stack.end();
+ it = next_it)
+ {
+ ++next_it;
+ if (it->first.empty())
+ {
+ continue;
+ }
+
+ out_nodes_t::iterator found_it = mOutNodes.lower_bound(it->second);
+
+ // node with this name not yet written
+ if (found_it == mOutNodes.end() || mOutNodes.key_comp()(found_it->first, it->second))
+ {
+ // make an attribute if we are the last element on the name stack
+ bool is_attribute = next_it == name_stack.end();
+ LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
+ out_node->addChild(new_node);
+ mOutNodes.insert(found_it, std::make_pair(it->second, new_node));
+ out_node = new_node;
+ }
+ else
+ {
+ out_node = found_it->second;
+ }
+ }
+
+ return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
+}
+
+
+bool LLXUIParser::readBoolValue(void* val_ptr)
+{
+ S32 value;
+ bool success = mCurReadNode->getBoolValue(1, &value);
+ *((bool*)val_ptr) = (value != FALSE);
+ return success;
+}
+
+bool LLXUIParser::writeBoolValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setBoolValue(*((bool*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readStringValue(void* val_ptr)
+{
+ *((std::string*)val_ptr) = mCurReadNode->getSanitizedValue();
+ return true;
+}
+
+bool LLXUIParser::writeStringValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ const std::string* string_val = reinterpret_cast<const std::string*>(val_ptr);
+ if (string_val->find('\n') != std::string::npos
+ || string_val->size() > MAX_STRING_ATTRIBUTE_SIZE)
+ {
+ // don't write strings with newlines into attributes
+ std::string attribute_name = node->getName()->mString;
+ LLXMLNodePtr parent_node = node->mParent;
+ parent_node->deleteChild(node);
+ // write results in text contents of node
+ if (attribute_name == "value")
+ {
+ // "value" is implicit, just write to parent
+ node = parent_node;
+ }
+ else
+ {
+ // create a child that is not an attribute, but with same name
+ node = parent_node->createChild(attribute_name.c_str(), false);
+ }
+ }
+ node->setStringValue(*string_val);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU8Value(void* val_ptr)
+{
+ return mCurReadNode->getByteValue(1, (U8*)val_ptr);
+}
+
+bool LLXUIParser::writeU8Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U8*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS8Value(void* val_ptr)
+{
+ S32 value;
+ if(mCurReadNode->getIntValue(1, &value))
+ {
+ *((S8*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeS8Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S8*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU16Value(void* val_ptr)
+{
+ U32 value;
+ if(mCurReadNode->getUnsignedValue(1, &value))
+ {
+ *((U16*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeU16Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U16*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS16Value(void* val_ptr)
+{
+ S32 value;
+ if(mCurReadNode->getIntValue(1, &value))
+ {
+ *((S16*)val_ptr) = value;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeS16Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S16*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readU32Value(void* val_ptr)
+{
+ return mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
+}
+
+bool LLXUIParser::writeU32Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setUnsignedValue(*((U32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readS32Value(void* val_ptr)
+{
+ return mCurReadNode->getIntValue(1, (S32*)val_ptr);
+}
+
+bool LLXUIParser::writeS32Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setIntValue(*((S32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readF32Value(void* val_ptr)
+{
+ return mCurReadNode->getFloatValue(1, (F32*)val_ptr);
+}
+
+bool LLXUIParser::writeF32Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setFloatValue(*((F32*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readF64Value(void* val_ptr)
+{
+ return mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
+}
+
+bool LLXUIParser::writeF64Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setDoubleValue(*((F64*)val_ptr));
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readColor4Value(void* val_ptr)
+{
+ LLColor4* colorp = (LLColor4*)val_ptr;
+ if(mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+bool LLXUIParser::writeColor4Value(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ LLColor4 color = *((LLColor4*)val_ptr);
+ node->setFloatValue(4, color.mV);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readUIColorValue(void* val_ptr)
+{
+ LLUIColor* param = (LLUIColor*)val_ptr;
+ LLColor4 color;
+ bool success = mCurReadNode->getFloatValue(4, color.mV) >= 3;
+ if (success)
+ {
+ param->set(color);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeUIColorValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ LLUIColor color = *((LLUIColor*)val_ptr);
+ //RN: don't write out the color that is represented by a function
+ // rely on param block exporting to get the reference to the color settings
+ if (color.isReference()) return false;
+ node->setFloatValue(4, color.get().mV);
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readUUIDValue(void* val_ptr)
+{
+ LLUUID temp_id;
+ // LLUUID::set is destructive, so use temporary value
+ if (temp_id.set(mCurReadNode->getSanitizedValue()))
+ {
+ *(LLUUID*)(val_ptr) = temp_id;
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::writeUUIDValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ node->setStringValue(((LLUUID*)val_ptr)->asString());
+ return true;
+ }
+ return false;
+}
+
+bool LLXUIParser::readSDValue(void* val_ptr)
+{
+ *((LLSD*)val_ptr) = LLSD(mCurReadNode->getSanitizedValue());
+ return true;
+}
+
+bool LLXUIParser::writeSDValue(const void* val_ptr, const name_stack_t& stack)
+{
+ LLXMLNodePtr node = getNode(stack);
+ if (node.notNull())
+ {
+ std::string string_val = ((LLSD*)val_ptr)->asString();
+ if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE)
+ {
+ // don't write strings with newlines into attributes
+ std::string attribute_name = node->getName()->mString;
+ LLXMLNodePtr parent_node = node->mParent;
+ parent_node->deleteChild(node);
+ // write results in text contents of node
+ if (attribute_name == "value")
+ {
+ // "value" is implicit, just write to parent
+ node = parent_node;
+ }
+ else
+ {
+ node = parent_node->createChild(attribute_name.c_str(), false);
+ }
+ }
+
+ node->setStringValue(string_val);
+ return true;
+ }
+ return false;
+}
+
+/*virtual*/ std::string LLXUIParser::getCurrentElementName()
+{
+ std::string full_name;
+ for (name_stack_t::iterator it = mNameStack.begin();
+ it != mNameStack.end();
+ ++it)
+ {
+ full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
+ }
+
+ return full_name;
+}
+
+void LLXUIParser::parserWarning(const std::string& message)
+{
+#if 0 //#ifdef LL_WINDOWS
+ // use Visual Studo friendly formatting of output message for easy access to originating xml
+ llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
+ utf16str += '\n';
+ OutputDebugString(utf16str.c_str());
+#else
+ Parser::parserWarning(message);
+#endif
+}
+
+void LLXUIParser::parserError(const std::string& message)
+{
+#if 0 //#ifdef LL_WINDOWS
+ llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", LLUICtrlFactory::getInstance()->getCurFileName().c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
+ utf16str += '\n';
+ OutputDebugString(utf16str.c_str());
+#else
+ Parser::parserError(message);
+#endif
+}
diff --git a/indra/llxuixml/llxuiparser.h b/indra/llxuixml/llxuiparser.h
new file mode 100644
index 0000000000..6f000f2422
--- /dev/null
+++ b/indra/llxuixml/llxuiparser.h
@@ -0,0 +1,174 @@
+/**
+ * @file llxuiparser.h
+ * @brief Utility functions for handling XUI structures in XML
+ *
+ * $LicenseInfo:firstyear=2003&license=viewergpl$
+ *
+ * Copyright (c) 2003-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LLXUIPARSER_H
+#define LLXUIPARSER_H
+
+#include "llinitparam.h"
+#include "llxmlnode.h"
+#include "llfasttimer.h"
+#include "llregistry.h"
+
+#include <boost/function.hpp>
+#include <iosfwd>
+#include <stack>
+#include <set>
+
+
+
+class LLView;
+
+
+
+// lookup widget type by name
+class LLWidgetTypeRegistry
+: public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
+{};
+
+
+// global static instance for registering all widget types
+typedef boost::function<LLView* (LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node)> LLWidgetCreatorFunc;
+
+typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
+
+class LLChildRegistryRegistry
+: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
+{};
+
+
+
+class LLXSDWriter : public LLInitParam::Parser
+{
+ LOG_CLASS(LLXSDWriter);
+public:
+ void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
+
+ /*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
+
+ LLXSDWriter();
+
+protected:
+ void writeAttribute(const std::string& type, const Parser::name_stack_t&, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values);
+ void addAttributeToSchema(LLXMLNodePtr nodep, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values);
+ LLXMLNodePtr mAttributeNode;
+ LLXMLNodePtr mElementNode;
+ LLXMLNodePtr mSchemaNode;
+
+ typedef std::set<std::string> string_set_t;
+ typedef std::map<LLXMLNodePtr, string_set_t> attributes_map_t;
+ attributes_map_t mAttributesWritten;
+};
+
+
+
+// NOTE: DOES NOT WORK YET
+// should support child widgets for XUI
+class LLXUIXSDWriter : public LLXSDWriter
+{
+public:
+ void writeXSD(const std::string& name, const std::string& path, const LLInitParam::BaseBlock& block);
+};
+
+
+
+class LLXUIParser : public LLInitParam::Parser, public LLSingleton<LLXUIParser>
+{
+LOG_CLASS(LLXUIParser);
+
+protected:
+ LLXUIParser();
+ friend class LLSingleton<LLXUIParser>;
+public:
+ typedef LLInitParam::Parser::name_stack_t name_stack_t;
+
+ /*virtual*/ std::string getCurrentElementName();
+ /*virtual*/ void parserWarning(const std::string& message);
+ /*virtual*/ void parserError(const std::string& message);
+
+ void readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent=false);
+ void writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const LLInitParam::BaseBlock* diff_block = NULL);
+
+private:
+ typedef std::list<std::pair<std::string, bool> > token_list_t;
+
+ bool readXUIImpl(LLXMLNodePtr node, const std::string& scope, LLInitParam::BaseBlock& block);
+ bool readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block);
+
+ //reader helper functions
+ bool readBoolValue(void* val_ptr);
+ bool readStringValue(void* val_ptr);
+ bool readU8Value(void* val_ptr);
+ bool readS8Value(void* val_ptr);
+ bool readU16Value(void* val_ptr);
+ bool readS16Value(void* val_ptr);
+ bool readU32Value(void* val_ptr);
+ bool readS32Value(void* val_ptr);
+ bool readF32Value(void* val_ptr);
+ bool readF64Value(void* val_ptr);
+ bool readColor4Value(void* val_ptr);
+ bool readUIColorValue(void* val_ptr);
+ bool readUUIDValue(void* val_ptr);
+ bool readSDValue(void* val_ptr);
+
+ //writer helper functions
+ bool writeBoolValue(const void* val_ptr, const name_stack_t&);
+ bool writeStringValue(const void* val_ptr, const name_stack_t&);
+ bool writeU8Value(const void* val_ptr, const name_stack_t&);
+ bool writeS8Value(const void* val_ptr, const name_stack_t&);
+ bool writeU16Value(const void* val_ptr, const name_stack_t&);
+ bool writeS16Value(const void* val_ptr, const name_stack_t&);
+ bool writeU32Value(const void* val_ptr, const name_stack_t&);
+ bool writeS32Value(const void* val_ptr, const name_stack_t&);
+ bool writeF32Value(const void* val_ptr, const name_stack_t&);
+ bool writeF64Value(const void* val_ptr, const name_stack_t&);
+ bool writeColor4Value(const void* val_ptr, const name_stack_t&);
+ bool writeUIColorValue(const void* val_ptr, const name_stack_t&);
+ bool writeUUIDValue(const void* val_ptr, const name_stack_t&);
+ bool writeSDValue(const void* val_ptr, const name_stack_t&);
+
+ LLXMLNodePtr getNode(const name_stack_t& stack);
+
+private:
+ Parser::name_stack_t mNameStack;
+ LLXMLNodePtr mCurReadNode;
+ // Root of the widget XML sub-tree, for example, "line_editor"
+ LLXMLNodePtr mWriteRootNode;
+
+ typedef std::map<S32, LLXMLNodePtr> out_nodes_t;
+ out_nodes_t mOutNodes;
+ S32 mLastWriteGeneration;
+ LLXMLNodePtr mLastWrittenChild;
+ S32 mCurReadDepth;
+};
+
+
+#endif //LLXUIPARSER_H
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index ffdacee22a..d7eabf33c4 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -26,6 +26,7 @@ include(LLUI)
include(LLVFS)
include(LLWindow)
include(LLXML)
+include(LLXUIXML)
include(LScript)
include(Linking)
include(Mozlib)
@@ -58,6 +59,7 @@ include_directories(
${LLVFS_INCLUDE_DIRS}
${LLWINDOW_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
+ ${LLXUIXML_INCLUDE_DIRS}
${LSCRIPT_INCLUDE_DIRS}
${LSCRIPT_INCLUDE_DIRS}/lscript_compile
)
@@ -1343,6 +1345,7 @@ target_link_libraries(${VIEWER_BINARY_NAME}
${LLVFS_LIBRARIES}
${LLWINDOW_LIBRARIES}
${LLXML_LIBRARIES}
+ ${LLXUIXML_LIBRARIES}
${LSCRIPT_LIBRARIES}
${LLMATH_LIBRARIES}
${LLCOMMON_LIBRARIES}
@@ -1402,6 +1405,7 @@ if (LINUX)
if (NOT INSTALL)
add_custom_target(package ALL DEPENDS ${product}.tar.bz2)
add_dependencies(package linux-crash-logger-strip-target)
+ add_dependencies(package linux-updater-strip-target)
endif (NOT INSTALL)
endif (LINUX)
diff --git a/indra/newview/linux_tools/client-readme.txt b/indra/newview/linux_tools/client-readme.txt
index 99c973f7ea..07a8f951ee 100644
--- a/indra/newview/linux_tools/client-readme.txt
+++ b/indra/newview/linux_tools/client-readme.txt
@@ -75,8 +75,9 @@ Life Linux client is very similar to that for Windows, as detailed at:
3. INSTALLING & RUNNING
-=-=-=-=-=-=-=-=-=-=-=-
-The Second Life Linux client entirely runs out of the directory you have
-unpacked it into - no installation step is required.
+The Second Life Linux client can entirely run from the directory you have
+unpacked it into - no installation step is required. If you wish to
+perform a separate installation step anyway, you may run './install.sh'
Run ./secondlife from the installation directory to start Second Life.
@@ -96,10 +97,7 @@ you wish.
4. KNOWN ISSUES
-=-=-=-=-=-=-=-
-* UPDATING - when the client detects that a new version of Second Life
- is available, it will ask you if you wish to download the new version.
- This option is not implemented; to upgrade, you should manually download a
- new version from the Second Life web site, <http://www.secondlife.com/>.
+* No significant known issues at this time.
5. TROUBLESHOOTING
diff --git a/indra/newview/linux_tools/handle_secondlifeprotocol.sh b/indra/newview/linux_tools/handle_secondlifeprotocol.sh
index 7ff86d1b93..203012132e 100755
--- a/indra/newview/linux_tools/handle_secondlifeprotocol.sh
+++ b/indra/newview/linux_tools/handle_secondlifeprotocol.sh
@@ -11,7 +11,7 @@ if [ -z "$URL" ]; then
fi
RUN_PATH=`dirname "$0" || echo .`
-cd "${RUN_PATH}"
+cd "${RUN_PATH}/.."
exec ./secondlife -url \'"${URL}"\'
diff --git a/indra/newview/linux_tools/install.sh b/indra/newview/linux_tools/install.sh
new file mode 100755
index 0000000000..c94510267a
--- /dev/null
+++ b/indra/newview/linux_tools/install.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+
+# Install the Second Life Viewer. This script can install the viewer both
+# system-wide and for an individual user.
+
+VT102_STYLE_NORMAL='\E[0m'
+VT102_COLOR_RED='\E[31m'
+
+SCRIPTSRC=`readlink -f "$0" || echo "$0"`
+RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
+tarball_path=${RUN_PATH}
+
+function prompt()
+{
+ local prompt=$1
+ local input
+
+ echo -n "$prompt"
+
+ while read input; do
+ case $input in
+ [Yy]* )
+ return 1
+ ;;
+ [Nn]* )
+ return 0
+ ;;
+ * )
+ echo "Please enter yes or no."
+ echo -n "$prompt"
+ esac
+ done
+}
+
+function die()
+{
+ warn $1
+ exit 1
+}
+
+function warn()
+{
+ echo -n -e $VT102_COLOR_RED
+ echo $1
+ echo -n -e $VT102_STYLE_NORMAL
+}
+
+function homedir_install()
+{
+ warn "You are not running as a privileged user, so you will only be able"
+ warn "to install the Second Life Viewer in your home directory. If you"
+ warn "would like to install the Second Life Viewer system-wide, please run"
+ warn "this script as the root user, or with the 'sudo' command."
+ echo
+
+ prompt "Proceed with the installation? [Y/N]: "
+ if [[ $? == 0 ]]; then
+ exit 0
+ fi
+
+ install_to_prefix "$HOME/.secondlife-install"
+ $HOME/.secondlife-install/etc/refresh_desktop_app_entry.sh
+}
+
+function root_install()
+{
+ local default_prefix="/opt/secondlife-install"
+
+ echo -n "Enter the desired installation directory [${default_prefix}]: ";
+ read
+ if [[ "$REPLY" = "" ]] ; then
+ local install_prefix=$default_prefix
+ else
+ local install_prefix=$REPLY
+ fi
+
+ install_to_prefix "$install_prefix"
+
+ mkdir -p /usr/local/share/applications
+ ${install_prefix}/etc/refresh_desktop_app_entry.sh
+}
+
+function install_to_prefix()
+{
+ test -e "$1" && backup_previous_installation "$1"
+ mkdir -p "$1" || die "Failed to create installation directory!"
+
+ echo " - Installing to $1"
+
+ cp -a "${tarball_path}"/* "$1/" || die "Failed to complete the installation!"
+}
+
+function backup_previous_installation()
+{
+ local backup_dir="$1".backup-$(date -I)
+ echo " - Backing up previous installation to $backup_dir"
+
+ mv "$1" "$backup_dir" || die "Failed to create backup of existing installation!"
+}
+
+
+if [ "$UID" == "0" ]; then
+ root_install
+else
+ homedir_install
+fi
diff --git a/indra/newview/linux_tools/refresh_desktop_app_entry.sh b/indra/newview/linux_tools/refresh_desktop_app_entry.sh
new file mode 100755
index 0000000000..d2b2a732d5
--- /dev/null
+++ b/indra/newview/linux_tools/refresh_desktop_app_entry.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+SCRIPTSRC=`readlink -f "$0" || echo "$0"`
+RUN_PATH=`dirname "${SCRIPTSRC}" || echo .`
+
+install_prefix=${RUN_PATH}/..
+
+function install_desktop_entry()
+{
+ local installation_prefix="$1"
+ local desktop_entries_dir="$2"
+
+ local desktop_entry="\
+[Desktop Entry]\n\
+Name=Second Life\n\
+Comment=Client for the On-line Virtual World, Second Life\n\
+Exec=${installation_prefix}/secondlife\n\
+Icon=${installation_prefix}/secondlife_icon.png\n\
+Terminal=false\n\
+Type=Application\n\
+Categories=Application;Network;\n\
+StartupNotify=true\n\
+X-Desktop-File-Install-Version=3.0"
+
+ echo " - Installing menu entries in ${desktop_entries_dir}"
+ mkdir -vp "${desktop_entries_dir}"
+ echo -e $desktop_entry > "${desktop_entries_dir}/secondlife-viewer.desktop" || "Failed to install application menu!"
+}
+
+if [ "$UID" == "0" ]; then
+ # system-wide
+ install_desktop_entry "$install_prefix" /usr/local/share/applications
+else
+ # user-specific
+ install_desktop_entry "$install_prefix" "$HOME/.local/share/applications"
+fi
diff --git a/indra/newview/linux_tools/register_secondlifeprotocol.sh b/indra/newview/linux_tools/register_secondlifeprotocol.sh
index 4ab96f97d6..c7b4d55461 100755
--- a/indra/newview/linux_tools/register_secondlifeprotocol.sh
+++ b/indra/newview/linux_tools/register_secondlifeprotocol.sh
@@ -7,10 +7,10 @@
HANDLER="$1"
RUN_PATH=`dirname "$0" || echo .`
-cd "${RUN_PATH}"
+cd "${RUN_PATH}/.."
if [ -z "$HANDLER" ]; then
- HANDLER=`pwd`/handle_secondlifeprotocol.sh
+ HANDLER=`pwd`/etc/handle_secondlifeprotocol.sh
fi
# Register handler for GNOME-aware apps
diff --git a/indra/newview/linux_tools/wrapper.sh b/indra/newview/linux_tools/wrapper.sh
index e188abe5d2..3209654498 100755
--- a/indra/newview/linux_tools/wrapper.sh
+++ b/indra/newview/linux_tools/wrapper.sh
@@ -91,12 +91,14 @@ echo "Running from ${RUN_PATH}"
cd "${RUN_PATH}"
# Re-register the secondlife:// protocol handler every launch, for now.
-./register_secondlifeprotocol.sh
+./etc/register_secondlifeprotocol.sh
+
+# Re-register the application with the desktop system every launch, for now.
+./etc/refresh_desktop_app_entry.sh
+
## Before we mess with LD_LIBRARY_PATH, save the old one to restore for
## subprocesses that care.
-if [ "${LD_LIBRARY_PATH+isset}" = "isset" ]; then
- export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
-fi
+export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
if [ -n "$LL_TCMALLOC" ]; then
tcmalloc_libs='/usr/lib/libtcmalloc.so.0 /usr/lib/libstacktrace.so.0 /lib/libpthread.so.0'
@@ -118,7 +120,7 @@ fi
export SL_ENV='LD_LIBRARY_PATH="`pwd`"/lib:"`pwd`"/app_settings/mozilla-runtime-linux-i686:"${LD_LIBRARY_PATH}"'
export SL_CMD='$LL_WRAPPER bin/do-not-directly-run-secondlife-bin'
-export SL_OPT="`cat gridargs.dat` $@"
+export SL_OPT="`cat etc/gridargs.dat` $@"
# Run the program
eval ${SL_ENV} ${SL_CMD} ${SL_OPT} || LL_RUN_ERR=runerr
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index a1484b3c52..5045f18784 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -113,6 +113,7 @@
#include "llviewermenu.h"
#include "llselectmgr.h"
#include "lltrans.h"
+#include "lltransutil.h"
#include "lltracker.h"
#include "llviewerparcelmgr.h"
#include "llworldmapview.h"
@@ -677,8 +678,8 @@ bool LLAppViewer::init()
// Setup paths and LLTrans after LLUI::initClass has been called
LLUI::setupPaths();
- LLTrans::parseStrings("strings.xml", default_trans_args);
- LLTrans::parseLanguageStrings("language_settings.xml");
+ LLTransUtil::parseStrings("strings.xml", default_trans_args);
+ LLTransUtil::parseLanguageStrings("language_settings.xml");
LLWeb::initClass(); // do this after LLUI
LLTextEditor::setURLCallbacks(&LLWeb::loadURL,
@@ -1740,8 +1741,8 @@ bool LLAppViewer::initConfiguration()
}
LLUI::setupPaths(); // setup paths for LLTrans based on settings files only
- LLTrans::parseStrings("strings.xml", default_trans_args);
- LLTrans::parseLanguageStrings("language_settings.xml");
+ LLTransUtil::parseStrings("strings.xml", default_trans_args);
+ LLTransUtil::parseLanguageStrings("language_settings.xml");
// - set procedural settings
// Note: can't use LL_PATH_PER_SL_ACCOUNT for any of these since we haven't logged in yet
gSavedSettings.setString("ClientSettingsFile",
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index d02e86a557..cd35c28aa7 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -553,7 +553,7 @@ void LLAppViewerLinux::handleSyncCrashTrace()
void LLAppViewerLinux::handleCrashReporting(bool reportFreeze)
{
- std::string cmd =gDirUtilp->getAppRODataDir();
+ std::string cmd =gDirUtilp->getExecutableDir();
cmd += gDirUtilp->getDirDelimiter();
#if LL_LINUX
cmd += "linux-crash-logger.bin";
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 1fc2245a8f..04a3b52e98 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -40,6 +40,12 @@
# include <sys/stat.h> // mkdir()
#endif
+#if LL_LINUX && LL_GTK
+extern "C" {
+# include "glib.h" // g_spawn_command_line_async()
+}
+#endif
+
#include "audioengine.h"
#ifdef LL_FMOD
@@ -2872,10 +2878,12 @@ void update_app(BOOL mandatory, const std::string& auth_msg)
#if LL_WINDOWS
notification_name += "Windows";
-#else
+#elif LL_DARWIN
notification_name += "Mac";
+#else
+ notification_name += "Linux";
#endif
-
+
if (mandatory)
{
notification_name += "Mandatory";
@@ -2888,7 +2896,6 @@ void update_app(BOOL mandatory, const std::string& auth_msg)
}
LLNotifications::instance().add(notification_name, args, payload, update_dialog_callback);
-
}
bool update_dialog_callback(const LLSD& notification, const LLSD& response)
@@ -2920,6 +2927,13 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
}
return false;
}
+
+ // if a sim name was passed in via command line parameter (typically through a SLURL)
+ if ( LLURLSimString::sInstance.mSimString.length() )
+ {
+ // record the location to start at next time
+ gSavedSettings.setString("NextLoginLocation", LLURLSimString::sInstance.mSimString);
+ }
LLSD query_map = LLSD::emptyMap();
// *TODO place os string in a global constant
@@ -2980,13 +2994,6 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
return false;
}
- // if a sim name was passed in via command line parameter (typically through a SLURL)
- if ( LLURLSimString::sInstance.mSimString.length() )
- {
- // record the location to start at next time
- gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString );
- };
-
LLAppViewer::sUpdaterInfo->mParams << "-url \"" << update_url.asString() << "\"";
LL_DEBUGS("AppInit") << "Calling updater: " << LLAppViewer::sUpdaterInfo->mUpdateExePath << " " << LLAppViewer::sUpdaterInfo->mParams.str() << LL_ENDL;
@@ -2995,13 +3002,6 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
LLAppViewer::instance()->removeMarkerFile(); // In case updater fails
#elif LL_DARWIN
- // if a sim name was passed in via command line parameter (typically through a SLURL)
- if ( LLURLSimString::sInstance.mSimString.length() )
- {
- // record the location to start at next time
- gSavedSettings.setString( "NextLoginLocation", LLURLSimString::sInstance.mSimString );
- };
-
LLAppViewer::sUpdaterInfo->mUpdateExePath = "'";
LLAppViewer::sUpdaterInfo->mUpdateExePath += gDirUtilp->getAppRODataDir();
LLAppViewer::sUpdaterInfo->mUpdateExePath += "/mac-updater.app/Contents/MacOS/mac-updater' -url \"";
@@ -3015,7 +3015,49 @@ bool update_dialog_callback(const LLSD& notification, const LLSD& response)
// Run the auto-updater.
system(LLAppViewer::sUpdaterInfo->mUpdateExePath.c_str()); /* Flawfinder: ignore */
-#elif LL_LINUX || LL_SOLARIS
+#elif (LL_LINUX || LL_SOLARIS) && LL_GTK
+ // 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::string xml_search_paths;
+ std::vector<std::string>::const_iterator iter;
+ // 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
+ }
+ // build the overall command-line to run the updater correctly
+ update_exe_path =
+ gDirUtilp->getExecutableDir() + "/" + "linux-updater.bin" +
+ " --url \"" + update_url.asString() + "\"" +
+ " --name \"" + LLAppViewer::instance()->getSecondLifeTitle() + "\"" +
+ " --dest \"" + gDirUtilp->getAppRODataDir() + "\"" +
+ " --stringsdir \"" + xml_search_paths + "\"" +
+ " --stringsfile \"" + xml_strings_file + "\"";
+
+ LL_INFOS("AppInit") << "Calling updater: "
+ << update_exe_path << LL_ENDL;
+
+ // *TODO: we could use the gdk equivilant to ensure the updater
+ // gets started on the same screen.
+ GError *error = NULL;
+ if (!g_spawn_command_line_async(update_exe_path.c_str(), &error))
+ {
+ llerrs << "Failed to launch updater: "
+ << error->message
+ << llendl;
+ }
+ if (error)
+ g_error_free(error);
+#else
OSMessageBox(LLTrans::getString("MBNoAutoUpdate"), LLStringUtil::null, OSMB_OK);
#endif
LLAppViewer::instance()->forceQuit();
diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp
index ca9e89723c..7b01fe4280 100644
--- a/indra/newview/llviewertexturelist.cpp
+++ b/indra/newview/llviewertexturelist.cpp
@@ -32,6 +32,8 @@
#include "llviewerprecompiledheaders.h"
+#include <sys/stat.h>
+
#include "llviewertexturelist.h"
#include "imageids.h"
@@ -61,8 +63,7 @@
#include "llviewerstats.h"
#include "pipeline.h"
#include "llappviewer.h"
-#include "lluictrlfactory.h" // for LLXUIParser
-#include <sys/stat.h>
+#include "llxuiparser.h"
////////////////////////////////////////////////////////////////////////////
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 8460e98fa3..3083a7f689 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -2628,6 +2628,45 @@ This update is not required, but we suggest you install it to improve performanc
<notification
icon="alertmodal.tga"
+ name="DownloadLinuxMandatory"
+ type="alertmodal">
+A new version of [SECOND_LIFE] is available.
+[MESSAGE]
+You must download this update to use [SECOND_LIFE].
+ <usetemplate
+ name="okcancelbuttons"
+ notext="Quit"
+ yestext="Download"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="DownloadLinux"
+ type="alertmodal">
+An updated version of [SECOND_LIFE] is available.
+[MESSAGE]
+This update is not required, but we suggest you install it to improve performance and stability.
+ <usetemplate
+ name="okcancelbuttons"
+ notext="Continue"
+ yestext="Download"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
+ name="DownloadLinuxReleaseForDownload"
+ type="alertmodal">
+An updated version of [SECOND_LIFE] is available.
+[MESSAGE]
+This update is not required, but we suggest you install it to improve performance and stability.
+ <usetemplate
+ name="okcancelbuttons"
+ notext="Continue"
+ yestext="Download"/>
+ </notification>
+
+ <notification
+ icon="alertmodal.tga"
name="DownloadMacMandatory"
type="alertmodal">
A new version of [SECOND_LIFE] is available.
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index f89625c535..3d4ac94044 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -666,7 +666,7 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh
<string name="Any">Any</string>
<string name="You">You</string>
- <!-- puncutations -->
+ <!-- punctuations -->
<string name=":">:</string>
<string name=",">,</string>
<string name="...">...</string>
@@ -936,4 +936,37 @@ If you continue to receive this message, contact customer support.
<string name="poofy skirt">poofy skirt</string>
<string name="tight skirt">tight skirt</string>
<string name="wrinkles">wrinkles</string>
+
+ <!-- Strings used by the (currently Linux) auto-updater app -->
+ <string name="UpdaterWindowTitle">
+ [SECOND_LIFE_VIEWER] Update
+ </string>
+ <string name="UpdaterNowUpdating">
+ Now updating [SECOND_LIFE_VIEWER]...
+ </string>
+ <string name="UpdaterNowInstalling">
+ Installing [SECOND_LIFE_VIEWER]...
+ </string>
+ <string name="UpdaterUpdatingDescriptive">
+ Your [SECOND_LIFE_VIEWER] Viewer is being updated to the latest release. This may take some time, so please be patient.
+ </string>
+ <string name="UpdaterProgressBarTextWithEllipses">
+ Downloading update...
+ </string>
+ <string name="UpdaterProgressBarText">
+ Downloading update
+ </string>
+ <string name="UpdaterFailDownloadTitle">
+ Failed to download update
+ </string>
+ <string name="UpdaterFailUpdateDescriptive">
+ An error occurred while updating Second Life. Please download the latest version from www.secondlife.com.
+ </string>
+ <string name="UpdaterFailInstallTitle">
+ Failed to install update
+ </string>
+ <string name="UpdaterFailStartTitle">
+ Failed to start viewer
+ </string>
+
</strings>
diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py
index 54095f866f..9c122deba0 100755
--- a/indra/newview/viewer_manifest.py
+++ b/indra/newview/viewer_manifest.py
@@ -593,12 +593,15 @@ class LinuxManifest(ViewerManifest):
self.path("client-readme-voice.txt","README-linux-voice.txt")
self.path("client-readme-joystick.txt","README-linux-joystick.txt")
self.path("wrapper.sh","secondlife")
- self.path("handle_secondlifeprotocol.sh")
- self.path("register_secondlifeprotocol.sh")
+ self.path("handle_secondlifeprotocol.sh", "etc/handle_secondlifeprotocol.sh")
+ self.path("register_secondlifeprotocol.sh", "etc/register_secondlifeprotocol.sh")
+ self.path("refresh_desktop_app_entry.sh", "etc/refresh_desktop_app_entry.sh")
+ self.path("launch_url.sh","etc/launch_url.sh")
+ self.path("install.sh")
self.end_prefix("linux_tools")
# Create an appropriate gridargs.dat for this package, denoting required grid.
- self.put_in_file(self.flags_list(), 'gridargs.dat')
+ self.put_in_file(self.flags_list(), 'etc/gridargs.dat')
def package_finish(self):
@@ -659,8 +662,8 @@ class Linux_i686Manifest(LinuxManifest):
pass
self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
- self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
- self.path("linux_tools/launch_url.sh","launch_url.sh")
+ self.path("../linux_crash_logger/linux-crash-logger-stripped","bin/linux-crash-logger.bin")
+ self.path("../linux_updater/linux-updater-stripped", "bin/linux-updater.bin")
if self.prefix("res-sdl"):
self.path("*")
# recurse
@@ -702,7 +705,6 @@ class Linux_x86_64Manifest(LinuxManifest):
super(Linux_x86_64Manifest, self).construct()
self.path("secondlife-stripped","bin/do-not-directly-run-secondlife-bin")
self.path("../linux_crash_logger/linux-crash-logger-stripped","linux-crash-logger.bin")
- self.path("linux_tools/launch_url.sh","launch_url.sh")
if self.prefix("res-sdl"):
self.path("*")
# recurse