diff options
Diffstat (limited to 'indra')
590 files changed, 43030 insertions, 13901 deletions
diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 3601c223f0..261c0b17e2 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -43,7 +43,6 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llimage) add_subdirectory(${LIBS_OPEN_PREFIX}llimagej2coj) add_subdirectory(${LIBS_OPEN_PREFIX}llinventory) add_subdirectory(${LIBS_OPEN_PREFIX}llmath) -add_subdirectory(${LIBS_OPEN_PREFIX}llmedia) add_subdirectory(${LIBS_OPEN_PREFIX}llmessage) add_subdirectory(${LIBS_OPEN_PREFIX}llprimitive) add_subdirectory(${LIBS_OPEN_PREFIX}llrender) @@ -64,12 +63,23 @@ endif (WINDOWS AND EXISTS ${LIBS_CLOSED_DIR}copy_win_scripts) add_custom_target(viewer) if (VIEWER) add_subdirectory(${LIBS_OPEN_PREFIX}llcrashlogger) + add_subdirectory(${LIBS_OPEN_PREFIX}llplugin) add_subdirectory(${LIBS_OPEN_PREFIX}llui) + add_subdirectory(${LIBS_OPEN_PREFIX}llxuixml) add_subdirectory(${LIBS_OPEN_PREFIX}viewer_components) + # viewer media plugins + add_subdirectory(${LIBS_OPEN_PREFIX}media_plugins) + + # llplugin testbed code (is this the right way to include it?) + if (NOT LINUX) + add_subdirectory(${VIEWER_PREFIX}test_apps/llplugintest) + endif (NOT LINUX) + 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/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 658441dab1..3ce393b659 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -34,7 +34,7 @@ set(cmake_SOURCE_FILES FindXmlRpcEpi.cmake FMOD.cmake FreeType.cmake - GStreamer.cmake + GStreamer010Plugin.cmake GooglePerfTools.cmake JPEG.cmake LLAddBuildTest.cmake @@ -48,8 +48,8 @@ set(cmake_SOURCE_FILES LLInventory.cmake LLKDU.cmake LLMath.cmake - LLMedia.cmake LLMessage.cmake + LLPlugin.cmake LLPrimitive.cmake LLRender.cmake LLScene.cmake @@ -60,7 +60,6 @@ set(cmake_SOURCE_FILES LScript.cmake Linking.cmake MonoEmbed.cmake - Mozlib.cmake MySQL.cmake NDOF.cmake OPENAL.cmake @@ -70,7 +69,6 @@ set(cmake_SOURCE_FILES PNG.cmake Python.cmake Prebuilt.cmake - QuickTime.cmake RunBuildTest.cmake TemplateCheck.cmake Tut.cmake diff --git a/indra/cmake/GStreamer010Plugin.cmake b/indra/cmake/GStreamer010Plugin.cmake new file mode 100644 index 0000000000..0d334837d4 --- /dev/null +++ b/indra/cmake/GStreamer010Plugin.cmake @@ -0,0 +1,39 @@ +# -*- cmake -*- +include(Prebuilt) + +if (STANDALONE) + include(FindPkgConfig) + + pkg_check_modules(GSTREAMER010 REQUIRED gstreamer-0.10) + pkg_check_modules(GSTREAMER010_PLUGINS_BASE REQUIRED gstreamer-plugins-base-0.10) +elseif (LINUX) + use_prebuilt_binary(gstreamer) + # possible libxml should have its own .cmake file instead + use_prebuilt_binary(libxml) + set(GSTREAMER010_FOUND ON FORCE BOOL) + set(GSTREAMER010_PLUGINS_BASE_FOUND ON FORCE BOOL) + set(GSTREAMER010_INCLUDE_DIRS + ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/gstreamer-0.10 + ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/glib-2.0 + ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/libxml2 + ) + # We don't need to explicitly link against gstreamer itself, because + # LLMediaImplGStreamer probes for the system's copy at runtime. + set(GSTREAMER010_LIBRARIES + gobject-2.0 + gmodule-2.0 + dl + gthread-2.0 + rt + glib-2.0 + ) +endif (STANDALONE) + +if (GSTREAMER010_FOUND AND GSTREAMER010_PLUGINS_BASE_FOUND) + set(GSTREAMER010 ON CACHE BOOL "Build with GStreamer-0.10 streaming media support.") +endif (GSTREAMER010_FOUND AND GSTREAMER010_PLUGINS_BASE_FOUND) + +if (GSTREAMER010) + add_definitions(-DLL_GSTREAMER010_ENABLED=1) +endif (GSTREAMER010) + diff --git a/indra/cmake/Glui.cmake b/indra/cmake/Glui.cmake new file mode 100644 index 0000000000..f62a56856c --- /dev/null +++ b/indra/cmake/Glui.cmake @@ -0,0 +1,28 @@ +# -*- cmake -*- +include(Linking) +include(Prebuilt) + +if (STANDALONE) + set(GLUI OFF CACHE BOOL + "GLUI support for the llplugin/llmedia test apps.") +else (STANDALONE) + use_prebuilt_binary(glui) + set(GLUI ON CACHE BOOL + "GLUI support for the llplugin/llmedia test apps.") +endif (STANDALONE) + +if (LINUX) + set(GLUI ON CACHE BOOL + "llplugin media apps HACK for Linux.") +endif (LINUX) + +if (DARWIN OR LINUX) + set(GLUI_LIBRARY + glui) +endif (DARWIN OR LINUX) + +if (WINDOWS) + set(GLUI_LIBRARY + debug glui32.lib + optimized glui32.lib) +endif (WINDOWS) diff --git a/indra/cmake/Glut.cmake b/indra/cmake/Glut.cmake new file mode 100644 index 0000000000..314da30652 --- /dev/null +++ b/indra/cmake/Glut.cmake @@ -0,0 +1,19 @@ +# -*- cmake -*- +include(Linking) +include(Prebuilt) + +if (WINDOWS) + use_prebuilt_binary(freeglut) + set(GLUT_LIBRARY + debug freeglut_static.lib + optimized freeglut_static.lib) +endif (WINDOWS) + +if (LINUX) + FIND_LIBRARY(GLUT_LIBRARY glut) +endif (LINUX) + +if (DARWIN) + include(CMakeFindFrameworks) + find_library(GLUT_LIBRARY GLUT) +endif (DARWIN) diff --git a/indra/cmake/LLPlugin.cmake b/indra/cmake/LLPlugin.cmake new file mode 100644 index 0000000000..9722f16c3c --- /dev/null +++ b/indra/cmake/LLPlugin.cmake @@ -0,0 +1,8 @@ +# -*- cmake -*- + + +set(LLPLUGIN_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/llplugin + ) + +set(LLPLUGIN_LIBRARIES llplugin) 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/cmake/MediaPluginBase.cmake b/indra/cmake/MediaPluginBase.cmake new file mode 100644 index 0000000000..2be035b641 --- /dev/null +++ b/indra/cmake/MediaPluginBase.cmake @@ -0,0 +1,8 @@ +# -*- cmake -*- + + +set(MEDIA_PLUGIN_BASE_INCLUDE_DIRS + ${LIBS_OPEN_DIR}/media_plugins/base/ + ) + +set(MEDIA_PLUGIN_BASE_LIBRARIES media_plugin_base) diff --git a/indra/cmake/PluginAPI.cmake b/indra/cmake/PluginAPI.cmake new file mode 100644 index 0000000000..d1649e8248 --- /dev/null +++ b/indra/cmake/PluginAPI.cmake @@ -0,0 +1,16 @@ +# -*- cmake -*- + +if (WINDOWS) + set(PLUGIN_API_WINDOWS_LIBRARIES + wsock32 + ws2_32 + psapi + netapi32 + advapi32 + user32 + ) +else (WINDOWS) + set(PLUGIN_API_WINDOWS_LIBRARIES "") +endif (WINDOWS) + + diff --git a/indra/cmake/QuickTimePlugin.cmake b/indra/cmake/QuickTimePlugin.cmake new file mode 100644 index 0000000000..8afd8f304c --- /dev/null +++ b/indra/cmake/QuickTimePlugin.cmake @@ -0,0 +1,46 @@ +# -*- cmake -*- + +if(INSTALL_PROPRIETARY) + include(Prebuilt) + use_prebuilt_binary(quicktime) +endif(INSTALL_PROPRIETARY) + +if (DARWIN) + include(CMakeFindFrameworks) + find_library(QUICKTIME_LIBRARY QuickTime) +elseif (WINDOWS) + set(QUICKTIME_SDK_DIR "$ENV{PROGRAMFILES}/QuickTime SDK" + CACHE PATH "Location of the QuickTime SDK.") + + find_library(DEBUG_QUICKTIME_LIBRARY qtmlclient + PATHS + ${ARCH_PREBUILT_DIRS_DEBUG} + "${QUICKTIME_SDK_DIR}\\libraries" + ) + + find_library(RELEASE_QUICKTIME_LIBRARY qtmlclient + PATHS + ${ARCH_PREBUILT_DIRS_RELEASE} + "${QUICKTIME_SDK_DIR}\\libraries" + ) + + if (DEBUG_QUICKTIME_LIBRARY AND RELEASE_QUICKTIME_LIBRARY) + set(QUICKTIME_LIBRARY + optimized ${RELEASE_QUICKTIME_LIBRARY} + debug ${DEBUG_QUICKTIME_LIBRARY} + ) + + endif (DEBUG_QUICKTIME_LIBRARY AND RELEASE_QUICKTIME_LIBRARY) + + include_directories( + ${LIBS_PREBUILT_DIR}/${LL_ARCH_DIR}/include/quicktime + "${QUICKTIME_SDK_DIR}\\CIncludes" + ) +endif (DARWIN) + +mark_as_advanced(QUICKTIME_LIBRARY) + +if (QUICKTIME_LIBRARY) + set(QUICKTIME ON CACHE BOOL "Build with QuickTime streaming media support.") +endif (QUICKTIME_LIBRARY) + diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake new file mode 100644 index 0000000000..c84df2b839 --- /dev/null +++ b/indra/cmake/WebKitLibPlugin.cmake @@ -0,0 +1,62 @@ +# -*- cmake -*- +include(Linking) +include(Prebuilt) + +if (STANDALONE) + set(WEBKITLIBPLUGIN OFF CACHE BOOL + "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.") +else (STANDALONE) + use_prebuilt_binary(webkitlibplugin) + set(WEBKITLIBPLUGIN ON CACHE BOOL + "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.") +endif (STANDALONE) + +if (WINDOWS) + set(WEBKIT_PLUGIN_LIBRARIES + debug llwebkitlibd + debug QtWebKitd4 + debug QtOpenGLd4 + debug QtNetworkd4 + debug QtGuid4 + debug QtCored4 + debug qtmaind + optimized llwebkitlib + optimized QtWebKit4 + optimized QtOpenGL4 + optimized QtNetwork4 + optimized QtGui4 + optimized QtCore4 + optimized qtmain + ) +elseif (DARWIN) + set(WEBKIT_PLUGIN_LIBRARIES + optimized ${ARCH_PREBUILT_DIRS_RELEASE}/libllwebkitlib.dylib + debug ${ARCH_PREBUILT_DIRS_RELEASE}/libllwebkitlib.dylib + ) +elseif (LINUX) + set(WEBKIT_PLUGIN_LIBRARIES + llwebkitlib + + qgif +# qico + qjpeg +# qpng +# qtiff +# qsvg + +# QtSvg + QtWebKit + QtOpenGL + QtNetwork + QtGui + QtCore + + fontconfig + X11 + GL + +# sqlite3 +# Xi +# SM + ) +endif (WINDOWS) diff --git a/indra/develop.py b/indra/develop.py index 78d9ca9705..bdc277202e 100755 --- a/indra/develop.py +++ b/indra/develop.py @@ -384,16 +384,20 @@ class LinuxSetup(UnixSetup): if job_count is None: hosts, job_count = count_distcc_hosts() + hostname = socket.gethostname() if hosts == 1: - hostname = socket.gethostname() if hostname.startswith('station'): hosts, job_count = mk_distcc_hosts('station', 36, 2) os.environ['DISTCC_HOSTS'] = hosts if hostname.startswith('eniac'): hosts, job_count = mk_distcc_hosts('eniac', 71, 2) os.environ['DISTCC_HOSTS'] = hosts - if job_count > 12: - job_count = 12; + if hostname.startswith('build'): + max_jobs = 6 + else: + max_jobs = 12 + if job_count > max_jobs: + job_count = max_jobs; opts.extend(['-j', str(job_count)]) if targets: 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..b93890ab32 --- /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, + "%s", 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()); + gchar *argv[8] = { + tar_cmd, + const_cast<gchar*>("--strip"), const_cast<gchar*>("1"), + const_cast<gchar*>("-xjf"), + package_file_string_copy, + const_cast<gchar*>("-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/llaudio/CMakeLists.txt b/indra/llaudio/CMakeLists.txt index 235248ee73..80245fd569 100644 --- a/indra/llaudio/CMakeLists.txt +++ b/indra/llaudio/CMakeLists.txt @@ -4,15 +4,16 @@ project(llaudio) include(00-Common) include(Audio) +include(LLAudio) include(FMOD) include(OPENAL) include(LLCommon) include(LLMath) include(LLMessage) include(LLVFS) -include(LLMedia) include_directories( + ${LLAUDIO_INCLUDE_DIRS} ${FMOD_INCLUDE_DIR} ${LLCOMMON_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} @@ -24,42 +25,43 @@ include_directories( ${VORBIS_INCLUDE_DIRS} ${OPENAL_LIB_INCLUDE_DIRS} ${FREEAULT_LIB_INCLUDE_DIRS} - ${LLMEDIA_INCLUDE_DIRS} ) set(llaudio_SOURCE_FILES - audioengine.cpp - listener.cpp + llaudioengine.cpp + lllistener.cpp llaudiodecodemgr.cpp - vorbisdecode.cpp - vorbisencode.cpp + llvorbisdecode.cpp + llvorbisencode.cpp ) set(llaudio_HEADER_FILES CMakeLists.txt - audioengine.h - listener.h + llaudioengine.h + lllistener.h llaudiodecodemgr.h - vorbisdecode.h - vorbisencode.h - windgen.h + llvorbisdecode.h + llvorbisencode.h + llwindgen.h ) if (FMOD) list(APPEND llaudio_SOURCE_FILES - audioengine_fmod.cpp - listener_fmod.cpp + llaudioengine_fmod.cpp + lllistener_fmod.cpp + llstreamingaudio_fmod.cpp ) list(APPEND llaudio_HEADER_FILES - audioengine_fmod.h - listener_fmod.h + llaudioengine_fmod.h + lllistener_fmod.h + llstreamingaudio_fmod.h ) if (LINUX) if (${CXX_VERSION} MATCHES "4.[23]") - set_source_files_properties(audioengine_fmod.cpp + set_source_files_properties(llaudioengine_fmod.cpp COMPILE_FLAGS -Wno-error=write-strings) endif (${CXX_VERSION} MATCHES "4.[23]") endif (LINUX) @@ -67,13 +69,13 @@ endif (FMOD) if (OPENAL) list(APPEND llaudio_SOURCE_FILES - audioengine_openal.cpp - listener_openal.cpp + llaudioengine_openal.cpp + lllistener_openal.cpp ) list(APPEND llaudio_HEADER_FILES - audioengine_openal.h - listener_openal.h + llaudioengine_openal.h + lllistener_openal.h ) endif (OPENAL) diff --git a/indra/llaudio/llaudiodecodemgr.cpp b/indra/llaudio/llaudiodecodemgr.cpp index 6a494d1983..099c4eba40 100644 --- a/indra/llaudio/llaudiodecodemgr.cpp +++ b/indra/llaudio/llaudiodecodemgr.cpp @@ -33,8 +33,8 @@ #include "llaudiodecodemgr.h" -#include "vorbisdecode.h" -#include "audioengine.h" +#include "llvorbisdecode.h" +#include "llaudioengine.h" #include "lllfsthread.h" #include "llvfile.h" #include "llstring.h" diff --git a/indra/llaudio/llaudioengine.cpp b/indra/llaudio/llaudioengine.cpp new file mode 100644 index 0000000000..a28c94d00d --- /dev/null +++ b/indra/llaudio/llaudioengine.cpp @@ -0,0 +1,1751 @@ + /** + * @file audioengine.cpp + * @brief implementation of LLAudioEngine class abstracting the Open + * AL audio support + * + * $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 "llaudioengine.h" +#include "llstreamingaudio.h" + +#include "llerror.h" +#include "llmath.h" + +#include "sound_ids.h" // temporary hack for min/max distances + +#include "llvfs.h" +#include "lldir.h" +#include "llaudiodecodemgr.h" +#include "llassetstorage.h" + + +// necessary for grabbing sounds from sim (implemented in viewer) +extern void request_sound(const LLUUID &sound_guid); + +LLAudioEngine* gAudiop = NULL; + + +// +// LLAudioEngine implementation +// + + +LLAudioEngine::LLAudioEngine() +{ + setDefaults(); +} + + +LLAudioEngine::~LLAudioEngine() +{ +} + +LLStreamingAudioInterface* LLAudioEngine::getStreamingAudioImpl() +{ + return mStreamingAudioImpl; +} + +void LLAudioEngine::setStreamingAudioImpl(LLStreamingAudioInterface *impl) +{ + mStreamingAudioImpl = impl; +} + +void LLAudioEngine::setDefaults() +{ + mMaxWindGain = 1.f; + + mListenerp = NULL; + + mMuted = false; + mUserData = NULL; + + mLastStatus = 0; + + mNumChannels = 0; + mEnableWind = false; + + S32 i; + for (i = 0; i < MAX_CHANNELS; i++) + { + mChannels[i] = NULL; + } + for (i = 0; i < MAX_BUFFERS; i++) + { + mBuffers[i] = NULL; + } + + mMasterGain = 1.f; + mNextWindUpdate = 0.f; + + mStreamingAudioImpl = NULL; + + for (U32 i = 0; i < LLAudioEngine::AUDIO_TYPE_COUNT; i++) + mSecondaryGain[i] = 1.0f; +} + + +bool LLAudioEngine::init(const S32 num_channels, void* userdata) +{ + setDefaults(); + + mNumChannels = num_channels; + mUserData = userdata; + + allocateListener(); + + // Initialize the decode manager + gAudioDecodeMgrp = new LLAudioDecodeMgr; + + llinfos << "LLAudioEngine::init() AudioEngine successfully initialized" << llendl; + + return true; +} + + +void LLAudioEngine::shutdown() +{ + // Clean up decode manager + delete gAudioDecodeMgrp; + gAudioDecodeMgrp = NULL; + + // Clean up wind source + cleanupWind(); + + // Clean up audio sources + source_map::iterator iter_src; + for (iter_src = mAllSources.begin(); iter_src != mAllSources.end(); iter_src++) + { + delete iter_src->second; + } + + + // Clean up audio data + data_map::iterator iter_data; + for (iter_data = mAllData.begin(); iter_data != mAllData.end(); iter_data++) + { + delete iter_data->second; + } + + + // Clean up channels + S32 i; + for (i = 0; i < MAX_CHANNELS; i++) + { + delete mChannels[i]; + mChannels[i] = NULL; + } + + // Clean up buffers + for (i = 0; i < MAX_BUFFERS; i++) + { + delete mBuffers[i]; + mBuffers[i] = NULL; + } +} + + +// virtual +void LLAudioEngine::startInternetStream(const std::string& url) +{ + if (mStreamingAudioImpl) + mStreamingAudioImpl->start(url); +} + + +// virtual +void LLAudioEngine::stopInternetStream() +{ + if (mStreamingAudioImpl) + mStreamingAudioImpl->stop(); +} + +// virtual +void LLAudioEngine::pauseInternetStream(int pause) +{ + if (mStreamingAudioImpl) + mStreamingAudioImpl->pause(pause); +} + +// virtual +void LLAudioEngine::updateInternetStream() +{ + if (mStreamingAudioImpl) + mStreamingAudioImpl->update(); +} + +// virtual +int LLAudioEngine::isInternetStreamPlaying() +{ + if (mStreamingAudioImpl) + return mStreamingAudioImpl->isPlaying(); + + return 0; // Stopped +} + + +// virtual +void LLAudioEngine::setInternetStreamGain(F32 vol) +{ + if (mStreamingAudioImpl) + mStreamingAudioImpl->setGain(vol); +} + +// virtual +std::string LLAudioEngine::getInternetStreamURL() +{ + if (mStreamingAudioImpl) + return mStreamingAudioImpl->getURL(); + else return std::string(); +} + + +void LLAudioEngine::updateChannels() +{ + S32 i; + for (i = 0; i < MAX_CHANNELS; i++) + { + if (mChannels[i]) + { + mChannels[i]->updateBuffer(); + mChannels[i]->update3DPosition(); + mChannels[i]->updateLoop(); + } + } +} + +static const F32 default_max_decode_time = .002f; // 2 ms +void LLAudioEngine::idle(F32 max_decode_time) +{ + if (max_decode_time <= 0.f) + { + max_decode_time = default_max_decode_time; + } + + // "Update" all of our audio sources, clean up dead ones. + // Primarily does position updating, cleanup of unused audio sources. + // Also does regeneration of the current priority of each audio source. + + if (getMuted()) + { + setInternalGain(0.f); + } + else + { + setInternalGain(getMasterGain()); + } + + S32 i; + for (i = 0; i < MAX_BUFFERS; i++) + { + if (mBuffers[i]) + { + mBuffers[i]->mInUse = false; + } + } + + F32 max_priority = -1.f; + LLAudioSource *max_sourcep = NULL; // Maximum priority source without a channel + source_map::iterator iter; + for (iter = mAllSources.begin(); iter != mAllSources.end();) + { + LLAudioSource *sourcep = iter->second; + + // Update this source + sourcep->update(); + sourcep->updatePriority(); + + if (sourcep->isDone()) + { + // The source is done playing, clean it up. + delete sourcep; + mAllSources.erase(iter++); + continue; + } + + if (!sourcep->getChannel() && sourcep->getCurrentBuffer()) + { + // We could potentially play this sound if its priority is high enough. + if (sourcep->getPriority() > max_priority) + { + max_priority = sourcep->getPriority(); + max_sourcep = sourcep; + } + } + + // Move on to the next source + iter++; + } + + // Now, do priority-based organization of audio sources. + // All channels used, check priorities. + // Find channel with lowest priority + if (max_sourcep) + { + LLAudioChannel *channelp = getFreeChannel(max_priority); + if (channelp) + { + //llinfos << "Replacing source in channel due to priority!" << llendl; + max_sourcep->setChannel(channelp); + channelp->setSource(max_sourcep); + if (max_sourcep->isSyncSlave()) + { + // A sync slave, it doesn't start playing until it's synced up with the master. + // Flag this channel as waiting for sync, and return true. + channelp->setWaiting(true); + } + else + { + channelp->setWaiting(false); + channelp->play(); + } + } + } + + + // Do this BEFORE we update the channels + // Update the channels to sync up with any changes that the source made, + // such as changing what sound was playing. + updateChannels(); + + // Update queued sounds (switch to next queued data if the current has finished playing) + for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) + { + // This is lame, instead of this I could actually iterate through all the sources + // attached to each channel, since only those with active channels + // can have anything interesting happen with their queue? (Maybe not true) + LLAudioSource *sourcep = iter->second; + if (!sourcep->mQueuedDatap) + { + // Nothing queued, so we don't care. + continue; + } + + LLAudioChannel *channelp = sourcep->getChannel(); + if (!channelp) + { + // This sound isn't playing, so we just process move the queue + sourcep->mCurrentDatap = sourcep->mQueuedDatap; + sourcep->mQueuedDatap = NULL; + + // Reset the timer so the source doesn't die. + sourcep->mAgeTimer.reset(); + // Make sure we have the buffer set up if we just decoded the data + if (sourcep->mCurrentDatap) + { + updateBufferForData(sourcep->mCurrentDatap); + } + + // Actually play the associated data. + sourcep->setupChannel(); + channelp = sourcep->getChannel(); + if (channelp) + { + channelp->updateBuffer(); + sourcep->getChannel()->play(); + } + continue; + } + else + { + // Check to see if the current sound is done playing, or looped. + if (!channelp->isPlaying()) + { + sourcep->mCurrentDatap = sourcep->mQueuedDatap; + sourcep->mQueuedDatap = NULL; + + // Reset the timer so the source doesn't die. + sourcep->mAgeTimer.reset(); + + // Make sure we have the buffer set up if we just decoded the data + if (sourcep->mCurrentDatap) + { + updateBufferForData(sourcep->mCurrentDatap); + } + + // Actually play the associated data. + sourcep->setupChannel(); + channelp->updateBuffer(); + sourcep->getChannel()->play(); + } + else if (sourcep->isLoop()) + { + // It's a loop, we need to check and see if we're done with it. + if (channelp->mLoopedThisFrame) + { + sourcep->mCurrentDatap = sourcep->mQueuedDatap; + sourcep->mQueuedDatap = NULL; + + // Actually, should do a time sync so if we're a loop master/slave + // we don't drift away. + sourcep->setupChannel(); + sourcep->getChannel()->play(); + } + } + } + } + + // Lame, update the channels AGAIN. + // Update the channels to sync up with any changes that the source made, + // such as changing what sound was playing. + updateChannels(); + + // Hack! For now, just use a global sync master; + LLAudioSource *sync_masterp = NULL; + LLAudioChannel *master_channelp = NULL; + F32 max_sm_priority = -1.f; + for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) + { + LLAudioSource *sourcep = iter->second; + if (sourcep->isSyncMaster()) + { + if (sourcep->getPriority() > max_sm_priority) + { + sync_masterp = sourcep; + master_channelp = sync_masterp->getChannel(); + max_sm_priority = sourcep->getPriority(); + } + } + } + + if (master_channelp && master_channelp->mLoopedThisFrame) + { + // Synchronize loop slaves with their masters + // Update queued sounds (switch to next queued data if the current has finished playing) + for (iter = mAllSources.begin(); iter != mAllSources.end(); ++iter) + { + LLAudioSource *sourcep = iter->second; + + if (!sourcep->isSyncSlave()) + { + // Not a loop slave, we don't need to do anything + continue; + } + + LLAudioChannel *channelp = sourcep->getChannel(); + if (!channelp) + { + // Not playing, don't need to bother. + continue; + } + + if (!channelp->isPlaying()) + { + // Now we need to check if our loop master has just looped, and + // start playback if that's the case. + if (sync_masterp->getChannel()) + { + channelp->playSynced(master_channelp); + channelp->setWaiting(false); + } + } + } + } + + // Sync up everything that the audio engine needs done. + commitDeferredChanges(); + + // Flush unused buffers that are stale enough + for (i = 0; i < MAX_BUFFERS; i++) + { + if (mBuffers[i]) + { + if (!mBuffers[i]->mInUse && mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > 30.f) + { + //llinfos << "Flushing unused buffer!" << llendl; + mBuffers[i]->mAudioDatap->mBufferp = NULL; + delete mBuffers[i]; + mBuffers[i] = NULL; + } + } + } + + + // Clear all of the looped flags for the channels + for (i = 0; i < MAX_CHANNELS; i++) + { + if (mChannels[i]) + { + mChannels[i]->mLoopedThisFrame = false; + } + } + + // Decode audio files + gAudioDecodeMgrp->processQueue(max_decode_time); + + // Call this every frame, just in case we somehow + // missed picking it up in all the places that can add + // or request new data. + startNextTransfer(); + + updateInternetStream(); +} + + + +bool LLAudioEngine::updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid) +{ + if (!adp) + { + return false; + } + + // Update the audio buffer first - load a sound if we have it. + // Note that this could potentially cause us to waste time updating buffers + // for sounds that actually aren't playing, although this should be mitigated + // by the fact that we limit the number of buffers, and we flush buffers based + // on priority. + if (!adp->getBuffer()) + { + if (adp->hasDecodedData()) + { + adp->load(); + } + else if (adp->hasLocalData()) + { + if (audio_uuid.notNull()) + { + gAudioDecodeMgrp->addDecodeRequest(audio_uuid); + } + } + else + { + return false; + } + } + return true; +} + + +void LLAudioEngine::enableWind(bool enable) +{ + if (enable && (!mEnableWind)) + { + initWind(); + mEnableWind = enable; + } + else if (mEnableWind && (!enable)) + { + mEnableWind = enable; + cleanupWind(); + } +} + + +LLAudioBuffer * LLAudioEngine::getFreeBuffer() +{ + S32 i; + for (i = 0; i < MAX_BUFFERS; i++) + { + if (!mBuffers[i]) + { + mBuffers[i] = createBuffer(); + return mBuffers[i]; + } + } + + + // Grab the oldest unused buffer + F32 max_age = -1.f; + S32 buffer_id = -1; + for (i = 0; i < MAX_BUFFERS; i++) + { + if (mBuffers[i]) + { + if (!mBuffers[i]->mInUse) + { + if (mBuffers[i]->mLastUseTimer.getElapsedTimeF32() > max_age) + { + max_age = mBuffers[i]->mLastUseTimer.getElapsedTimeF32(); + buffer_id = i; + } + } + } + } + + if (buffer_id >= 0) + { + llinfos << "Taking over unused buffer " << buffer_id << llendl; + //llinfos << "Flushing unused buffer!" << llendl; + mBuffers[buffer_id]->mAudioDatap->mBufferp = NULL; + delete mBuffers[buffer_id]; + mBuffers[buffer_id] = createBuffer(); + return mBuffers[buffer_id]; + } + return NULL; +} + + +LLAudioChannel * LLAudioEngine::getFreeChannel(const F32 priority) +{ + S32 i; + for (i = 0; i < mNumChannels; i++) + { + if (!mChannels[i]) + { + // No channel allocated here, use it. + mChannels[i] = createChannel(); + return mChannels[i]; + } + else + { + // Channel is allocated but not playing right now, use it. + if (!mChannels[i]->isPlaying() && !mChannels[i]->isWaiting()) + { + mChannels[i]->cleanup(); + if (mChannels[i]->getSource()) + { + mChannels[i]->getSource()->setChannel(NULL); + } + return mChannels[i]; + } + } + } + + // All channels used, check priorities. + // Find channel with lowest priority and see if we want to replace it. + F32 min_priority = 10000.f; + LLAudioChannel *min_channelp = NULL; + + for (i = 0; i < mNumChannels; i++) + { + LLAudioChannel *channelp = mChannels[i]; + LLAudioSource *sourcep = channelp->getSource(); + if (sourcep->getPriority() < min_priority) + { + min_channelp = channelp; + min_priority = sourcep->getPriority(); + } + } + + if (min_priority > priority || !min_channelp) + { + // All playing channels have higher priority, return. + return NULL; + } + + // Flush the minimum priority channel, and return it. + min_channelp->cleanup(); + min_channelp->getSource()->setChannel(NULL); + return min_channelp; +} + + +void LLAudioEngine::cleanupBuffer(LLAudioBuffer *bufferp) +{ + S32 i; + for (i = 0; i < MAX_BUFFERS; i++) + { + if (mBuffers[i] == bufferp) + { + delete mBuffers[i]; + mBuffers[i] = NULL; + } + } +} + + +bool LLAudioEngine::preloadSound(const LLUUID &uuid) +{ + gAudiop->getAudioData(uuid); // We don't care about the return value, this is just to make sure + // that we have an entry, which will mean that the audio engine knows about this + + if (gAudioDecodeMgrp->addDecodeRequest(uuid)) + { + // This means that we do have a local copy, and we're working on decoding it. + return true; + } + + // At some point we need to have the audio/asset system check the static VFS + // before it goes off and fetches stuff from the server. + //llwarns << "Used internal preload for non-local sound" << llendl; + return false; +} + + +bool LLAudioEngine::isWindEnabled() +{ + return mEnableWind; +} + + +void LLAudioEngine::setMuted(bool muted) +{ + mMuted = muted; + enableWind(!mMuted); +} + + +void LLAudioEngine::setMasterGain(const F32 gain) +{ + mMasterGain = gain; + setInternalGain(gain); +} + +F32 LLAudioEngine::getMasterGain() +{ + return mMasterGain; +} + +void LLAudioEngine::setSecondaryGain(S32 type, F32 gain) +{ + llassert(type < LLAudioEngine::AUDIO_TYPE_COUNT); + + mSecondaryGain[type] = gain; +} + +F32 LLAudioEngine::getSecondaryGain(S32 type) +{ + return mSecondaryGain[type]; +} + +F32 LLAudioEngine::getInternetStreamGain() +{ + if (mStreamingAudioImpl) + return mStreamingAudioImpl->getGain(); + else + return 1.0f; +} + +void LLAudioEngine::setMaxWindGain(F32 gain) +{ + mMaxWindGain = gain; +} + + +F64 LLAudioEngine::mapWindVecToGain(LLVector3 wind_vec) +{ + F64 gain = 0.0; + + gain = wind_vec.magVec(); + + if (gain) + { + if (gain > 20) + { + gain = 20; + } + gain = gain/20.0; + } + + return (gain); +} + + +F64 LLAudioEngine::mapWindVecToPitch(LLVector3 wind_vec) +{ + LLVector3 listen_right; + F64 theta; + + // Wind frame is in listener-relative coordinates + LLVector3 norm_wind = wind_vec; + norm_wind.normVec(); + listen_right.setVec(1.0,0.0,0.0); + + // measure angle between wind vec and listener right axis (on 0,PI) + theta = acos(norm_wind * listen_right); + + // put it on 0, 1 + theta /= F_PI; + + // put it on [0, 0.5, 0] + if (theta > 0.5) theta = 1.0-theta; + if (theta < 0) theta = 0; + + return (theta); +} + + +F64 LLAudioEngine::mapWindVecToPan(LLVector3 wind_vec) +{ + LLVector3 listen_right; + F64 theta; + + // Wind frame is in listener-relative coordinates + listen_right.setVec(1.0,0.0,0.0); + + LLVector3 norm_wind = wind_vec; + norm_wind.normVec(); + + // measure angle between wind vec and listener right axis (on 0,PI) + theta = acos(norm_wind * listen_right); + + // put it on 0, 1 + theta /= F_PI; + + return (theta); +} + + +void LLAudioEngine::triggerSound(const LLUUID &audio_uuid, const LLUUID& owner_id, const F32 gain, + const S32 type, const LLVector3d &pos_global) +{ + // Create a new source (since this can't be associated with an existing source. + //llinfos << "Localized: " << audio_uuid << llendl; + + if (mMuted) + { + return; + } + + LLUUID source_id; + source_id.generate(); + + LLAudioSource *asp = new LLAudioSource(source_id, owner_id, gain, type); + gAudiop->addAudioSource(asp); + if (pos_global.isExactlyZero()) + { + asp->setAmbient(true); + } + else + { + asp->setPositionGlobal(pos_global); + } + asp->updatePriority(); + asp->play(audio_uuid); +} + + +void LLAudioEngine::setListenerPos(LLVector3 aVec) +{ + mListenerp->setPosition(aVec); +} + + +LLVector3 LLAudioEngine::getListenerPos() +{ + if (mListenerp) + { + return(mListenerp->getPosition()); + } + else + { + return(LLVector3::zero); + } +} + + +void LLAudioEngine::setListenerVelocity(LLVector3 aVec) +{ + mListenerp->setVelocity(aVec); +} + + +void LLAudioEngine::translateListener(LLVector3 aVec) +{ + mListenerp->translate(aVec); +} + + +void LLAudioEngine::orientListener(LLVector3 up, LLVector3 at) +{ + mListenerp->orient(up, at); +} + + +void LLAudioEngine::setListener(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at) +{ + mListenerp->set(pos,vel,up,at); +} + + +void LLAudioEngine::setDopplerFactor(F32 factor) +{ + if (mListenerp) + { + mListenerp->setDopplerFactor(factor); + } +} + + +F32 LLAudioEngine::getDopplerFactor() +{ + if (mListenerp) + { + return mListenerp->getDopplerFactor(); + } + else + { + return 0.f; + } +} + + +void LLAudioEngine::setRolloffFactor(F32 factor) +{ + if (mListenerp) + { + mListenerp->setRolloffFactor(factor); + } +} + + +F32 LLAudioEngine::getRolloffFactor() +{ + if (mListenerp) + { + return mListenerp->getRolloffFactor(); + } + else + { + return 0.f; + } +} + + +void LLAudioEngine::commitDeferredChanges() +{ + mListenerp->commitDeferredChanges(); +} + + +LLAudioSource * LLAudioEngine::findAudioSource(const LLUUID &source_id) +{ + source_map::iterator iter; + iter = mAllSources.find(source_id); + + if (iter == mAllSources.end()) + { + return NULL; + } + else + { + return iter->second; + } +} + + +LLAudioData * LLAudioEngine::getAudioData(const LLUUID &audio_uuid) +{ + data_map::iterator iter; + iter = mAllData.find(audio_uuid); + if (iter == mAllData.end()) + { + // Create the new audio data + LLAudioData *adp = new LLAudioData(audio_uuid); + mAllData[audio_uuid] = adp; + return adp; + } + else + { + return iter->second; + } +} + +void LLAudioEngine::addAudioSource(LLAudioSource *asp) +{ + mAllSources[asp->getID()] = asp; +} + + +void LLAudioEngine::cleanupAudioSource(LLAudioSource *asp) +{ + source_map::iterator iter; + iter = mAllSources.find(asp->getID()); + if (iter == mAllSources.end()) + { + llwarns << "Cleaning up unknown audio source!" << llendl; + return; + } + delete asp; + mAllSources.erase(iter); +} + + +bool LLAudioEngine::hasDecodedFile(const LLUUID &uuid) +{ + std::string uuid_str; + uuid.toString(uuid_str); + + std::string wav_path; + wav_path = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str); + wav_path += ".dsf"; + + if (gDirUtilp->fileExists(wav_path)) + { + return true; + } + else + { + return false; + } +} + + +bool LLAudioEngine::hasLocalFile(const LLUUID &uuid) +{ + // See if it's in the VFS. + return gVFS->getExists(uuid, LLAssetType::AT_SOUND); +} + + +void LLAudioEngine::startNextTransfer() +{ + //llinfos << "LLAudioEngine::startNextTransfer()" << llendl; + if (mCurrentTransfer.notNull() || getMuted()) + { + //llinfos << "Transfer in progress, aborting" << llendl; + return; + } + + // Get the ID for the next asset that we want to transfer. + // Pick one in the following order: + LLUUID asset_id; + S32 i; + LLAudioSource *asp = NULL; + LLAudioData *adp = NULL; + data_map::iterator data_iter; + + // Check all channels for currently playing sounds. + F32 max_pri = -1.f; + for (i = 0; i < MAX_CHANNELS; i++) + { + if (!mChannels[i]) + { + continue; + } + + asp = mChannels[i]->getSource(); + if (!asp) + { + continue; + } + if (asp->getPriority() <= max_pri) + { + continue; + } + + if (asp->getPriority() <= max_pri) + { + continue; + } + + adp = asp->getCurrentData(); + if (!adp) + { + continue; + } + + if (!adp->hasLocalData() && adp->hasValidData()) + { + asset_id = adp->getID(); + max_pri = asp->getPriority(); + } + } + + // Check all channels for currently queued sounds. + if (asset_id.isNull()) + { + max_pri = -1.f; + for (i = 0; i < MAX_CHANNELS; i++) + { + if (!mChannels[i]) + { + continue; + } + + LLAudioSource *asp; + asp = mChannels[i]->getSource(); + if (!asp) + { + continue; + } + + if (asp->getPriority() <= max_pri) + { + continue; + } + + adp = asp->getQueuedData(); + if (!adp) + { + continue; + } + + if (!adp->hasLocalData() && adp->hasValidData()) + { + asset_id = adp->getID(); + max_pri = asp->getPriority(); + } + } + } + + // Check all live channels for other sounds (preloads). + if (asset_id.isNull()) + { + max_pri = -1.f; + for (i = 0; i < MAX_CHANNELS; i++) + { + if (!mChannels[i]) + { + continue; + } + + LLAudioSource *asp; + asp = mChannels[i]->getSource(); + if (!asp) + { + continue; + } + + if (asp->getPriority() <= max_pri) + { + continue; + } + + + for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++) + { + LLAudioData *adp = data_iter->second; + if (!adp) + { + continue; + } + + if (!adp->hasLocalData() && adp->hasValidData()) + { + asset_id = adp->getID(); + max_pri = asp->getPriority(); + } + } + } + } + + // Check all sources + if (asset_id.isNull()) + { + max_pri = -1.f; + source_map::iterator source_iter; + for (source_iter = mAllSources.begin(); source_iter != mAllSources.end(); source_iter++) + { + asp = source_iter->second; + if (!asp) + { + continue; + } + + if (asp->getPriority() <= max_pri) + { + continue; + } + + adp = asp->getCurrentData(); + if (adp && !adp->hasLocalData() && adp->hasValidData()) + { + asset_id = adp->getID(); + max_pri = asp->getPriority(); + continue; + } + + adp = asp->getQueuedData(); + if (adp && !adp->hasLocalData() && adp->hasValidData()) + { + asset_id = adp->getID(); + max_pri = asp->getPriority(); + continue; + } + + for (data_iter = asp->mPreloadMap.begin(); data_iter != asp->mPreloadMap.end(); data_iter++) + { + LLAudioData *adp = data_iter->second; + if (!adp) + { + continue; + } + + if (!adp->hasLocalData() && adp->hasValidData()) + { + asset_id = adp->getID(); + max_pri = asp->getPriority(); + break; + } + } + } + } + + if (asset_id.notNull()) + { + llinfos << "Getting asset data for: " << asset_id << llendl; + gAudiop->mCurrentTransfer = asset_id; + gAudiop->mCurrentTransferTimer.reset(); + gAssetStorage->getAssetData(asset_id, LLAssetType::AT_SOUND, + assetCallback, NULL); + } + else + { + //llinfos << "No pending transfers?" << llendl; + } +} + + +// static +void LLAudioEngine::assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status) +{ + if (result_code) + { + llinfos << "Boom, error in audio file transfer: " << LLAssetStorage::getErrorString( result_code ) << " (" << result_code << ")" << llendl; + // Need to mark data as bad to avoid constant rerequests. + LLAudioData *adp = gAudiop->getAudioData(uuid); + if (adp) + { + adp->setHasValidData(false); + adp->setHasLocalData(false); + adp->setHasDecodedData(false); + } + } + else + { + LLAudioData *adp = gAudiop->getAudioData(uuid); + if (!adp) + { + // Should never happen + llwarns << "Got asset callback without audio data for " << uuid << llendl; + } + else + { + adp->setHasValidData(true); + adp->setHasLocalData(true); + gAudioDecodeMgrp->addDecodeRequest(uuid); + } + } + gAudiop->mCurrentTransfer = LLUUID::null; + gAudiop->startNextTransfer(); +} + + +// +// LLAudioSource implementation +// + + +LLAudioSource::LLAudioSource(const LLUUID& id, const LLUUID& owner_id, const F32 gain, const S32 type) +: mID(id), + mOwnerID(owner_id), + mPriority(0.f), + mGain(gain), + mType(type), + mAmbient(false), + mLoop(false), + mSyncMaster(false), + mSyncSlave(false), + mQueueSounds(false), + mPlayedOnce(false), + mChannelp(NULL), + mCurrentDatap(NULL), + mQueuedDatap(NULL) +{ +} + + +LLAudioSource::~LLAudioSource() +{ + if (mChannelp) + { + // Stop playback of this sound + mChannelp->setSource(NULL); + mChannelp = NULL; + } +} + + +void LLAudioSource::setChannel(LLAudioChannel *channelp) +{ + if (channelp == mChannelp) + { + return; + } + + mChannelp = channelp; +} + + +void LLAudioSource::update() +{ + if (!getCurrentBuffer()) + { + if (getCurrentData()) + { + // Hack - try and load the sound. Will do this as a callback + // on decode later. + if (getCurrentData()->load()) + { + play(getCurrentData()->getID()); + } + } + } +} + +void LLAudioSource::updatePriority() +{ + if (isAmbient()) + { + mPriority = 1.f; + } + else + { + // Priority is based on distance + LLVector3 dist_vec; + dist_vec.setVec(getPositionGlobal()); + dist_vec -= gAudiop->getListenerPos(); + F32 dist_squared = llmax(1.f, dist_vec.magVecSquared()); + + mPriority = mGain / dist_squared; + } +} + +bool LLAudioSource::setupChannel() +{ + LLAudioData *adp = getCurrentData(); + + if (!adp->getBuffer()) + { + // We're not ready to play back the sound yet, so don't try and allocate a channel for it. + //llwarns << "Aborting, no buffer" << llendl; + return false; + } + + + if (!mChannelp) + { + // Update the priority, in case we need to push out another channel. + updatePriority(); + + setChannel(gAudiop->getFreeChannel(getPriority())); + } + + if (!mChannelp) + { + // Ugh, we don't have any free channels. + // Now we have to reprioritize. + // For now, just don't play the sound. + //llwarns << "Aborting, no free channels" << llendl; + return false; + } + + mChannelp->setSource(this); + return true; +} + + +bool LLAudioSource::play(const LLUUID &audio_uuid) +{ + if (audio_uuid.isNull()) + { + if (getChannel()) + { + getChannel()->setSource(NULL); + setChannel(NULL); + addAudioData(NULL, true); + } + } + // Reset our age timeout if someone attempts to play the source. + mAgeTimer.reset(); + + LLAudioData *adp = gAudiop->getAudioData(audio_uuid); + + bool has_buffer = gAudiop->updateBufferForData(adp, audio_uuid); + + + addAudioData(adp); + + if (!has_buffer) + { + // Don't bother trying to set up a channel or anything, we don't have an audio buffer. + return false; + } + + if (!setupChannel()) + { + return false; + } + + if (isSyncSlave()) + { + // A sync slave, it doesn't start playing until it's synced up with the master. + // Flag this channel as waiting for sync, and return true. + getChannel()->setWaiting(true); + return true; + } + + getChannel()->play(); + return true; +} + + +bool LLAudioSource::isDone() +{ + const F32 MAX_AGE = 60.f; + const F32 MAX_UNPLAYED_AGE = 15.f; + + if (isLoop()) + { + // Looped sources never die on their own. + return false; + } + + + if (hasPendingPreloads()) + { + return false; + } + + if (mQueuedDatap) + { + // Don't kill this sound if we've got something queued up to play. + return false; + } + + F32 elapsed = mAgeTimer.getElapsedTimeF32(); + + // This is a single-play source + if (!mChannelp) + { + if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce) + { + // We don't have a channel assigned, and it's been + // over 5 seconds since we tried to play it. Don't bother. + //llinfos << "No channel assigned, source is done" << llendl; + return true; + } + else + { + return false; + } + } + + if (mChannelp->isPlaying()) + { + if (elapsed > MAX_AGE) + { + // Arbitarily cut off non-looped sounds when they're old. + return true; + } + else + { + // Sound is still playing and we haven't timed out, don't kill it. + return false; + } + } + + if ((elapsed > MAX_UNPLAYED_AGE) || mPlayedOnce) + { + // The sound isn't playing back after 5 seconds or we're already done playing it, kill it. + return true; + } + + return false; +} + + +void LLAudioSource::addAudioData(LLAudioData *adp, const bool set_current) +{ + // Only handle a single piece of audio data associated with a source right now, + // until I implement prefetch. + if (set_current) + { + if (!mCurrentDatap) + { + mCurrentDatap = adp; + if (mChannelp) + { + mChannelp->updateBuffer(); + mChannelp->play(); + } + + // Make sure the audio engine knows that we want to request this sound. + gAudiop->startNextTransfer(); + return; + } + else if (mQueueSounds) + { + // If we have current data, and we're queuing, put + // the object onto the queue. + if (mQueuedDatap) + { + // We only queue one sound at a time, and it's a FIFO. + // Don't put it onto the queue. + return; + } + + if (adp == mCurrentDatap && isLoop()) + { + // No point in queueing the same sound if + // we're looping. + return; + } + mQueuedDatap = adp; + + // Make sure the audio engine knows that we want to request this sound. + gAudiop->startNextTransfer(); + } + else + { + if (mCurrentDatap != adp) + { + // Right now, if we're currently playing this sound in a channel, we + // update the buffer that the channel's associated with + // and play it. This may not be the correct behavior. + mCurrentDatap = adp; + if (mChannelp) + { + mChannelp->updateBuffer(); + mChannelp->play(); + } + // Make sure the audio engine knows that we want to request this sound. + gAudiop->startNextTransfer(); + } + } + } + else + { + // Add it to the preload list. + mPreloadMap[adp->getID()] = adp; + gAudiop->startNextTransfer(); + } +} + + +bool LLAudioSource::hasPendingPreloads() const +{ + // Check to see if we've got any preloads on deck for this source + data_map::const_iterator iter; + for (iter = mPreloadMap.begin(); iter != mPreloadMap.end(); iter++) + { + LLAudioData *adp = iter->second; + // note: a bad UUID will forever be !hasDecodedData() + // but also !hasValidData(), hence the check for hasValidData() + if (!adp->hasDecodedData() && adp->hasValidData()) + { + // This source is still waiting for a preload + return true; + } + } + + return false; +} + + +LLAudioData * LLAudioSource::getCurrentData() +{ + return mCurrentDatap; +} + +LLAudioData * LLAudioSource::getQueuedData() +{ + return mQueuedDatap; +} + +LLAudioBuffer * LLAudioSource::getCurrentBuffer() +{ + if (!mCurrentDatap) + { + return NULL; + } + + return mCurrentDatap->getBuffer(); +} + + + + +// +// LLAudioChannel implementation +// + + +LLAudioChannel::LLAudioChannel() : + mCurrentSourcep(NULL), + mCurrentBufferp(NULL), + mLoopedThisFrame(false), + mWaiting(false), + mSecondaryGain(1.0f) +{ +} + + +LLAudioChannel::~LLAudioChannel() +{ + // Need to disconnect any sources which are using this channel. + //llinfos << "Cleaning up audio channel" << llendl; + if (mCurrentSourcep) + { + mCurrentSourcep->setChannel(NULL); + } + mCurrentBufferp = NULL; +} + + +void LLAudioChannel::setSource(LLAudioSource *sourcep) +{ + //llinfos << this << ": setSource(" << sourcep << ")" << llendl; + + if (!sourcep) + { + // Clearing the source for this channel, don't need to do anything. + //llinfos << "Clearing source for channel" << llendl; + cleanup(); + mCurrentSourcep = NULL; + mWaiting = false; + return; + } + + if (sourcep == mCurrentSourcep) + { + // Don't reallocate the channel, this will make FMOD goofy. + //llinfos << "Calling setSource with same source!" << llendl; + } + + mCurrentSourcep = sourcep; + + + updateBuffer(); + update3DPosition(); +} + + +bool LLAudioChannel::updateBuffer() +{ + if (!mCurrentSourcep) + { + // This channel isn't associated with any source, nothing + // to be updated + return false; + } + + // Initialize the channel's gain setting for this sound. + if(gAudiop) + { + setSecondaryGain(gAudiop->getSecondaryGain(mCurrentSourcep->getType())); + } + + LLAudioBuffer *bufferp = mCurrentSourcep->getCurrentBuffer(); + if (bufferp == mCurrentBufferp) + { + if (bufferp) + { + // The source hasn't changed what buffer it's playing + bufferp->mLastUseTimer.reset(); + bufferp->mInUse = true; + } + return false; + } + + // + // The source changed what buffer it's playing. We need to clean up + // the existing channel + // + cleanup(); + + mCurrentBufferp = bufferp; + if (bufferp) + { + bufferp->mLastUseTimer.reset(); + bufferp->mInUse = true; + } + + if (!mCurrentBufferp) + { + // There's no new buffer to be played, so we just abort. + return false; + } + + return true; +} + + + + +// +// LLAudioData implementation +// + + +LLAudioData::LLAudioData(const LLUUID &uuid) : + mID(uuid), + mBufferp(NULL), + mHasLocalData(false), + mHasDecodedData(false), + mHasValidData(true) +{ + if (uuid.isNull()) + { + // This is a null sound. + return; + } + + if (gAudiop && gAudiop->hasDecodedFile(uuid)) + { + // Already have a decoded version, don't need to decode it. + mHasLocalData = true; + mHasDecodedData = true; + } + else if (gAssetStorage && gAssetStorage->hasLocalAsset(uuid, LLAssetType::AT_SOUND)) + { + mHasLocalData = true; + } +} + + +bool LLAudioData::load() +{ + // For now, just assume we're going to use one buffer per audiodata. + if (mBufferp) + { + // We already have this sound in a buffer, don't do anything. + llinfos << "Already have a buffer for this sound, don't bother loading!" << llendl; + return true; + } + + mBufferp = gAudiop->getFreeBuffer(); + if (!mBufferp) + { + // No free buffers, abort. + llinfos << "Not able to allocate a new audio buffer, aborting." << llendl; + return false; + } + + std::string uuid_str; + std::string wav_path; + mID.toString(uuid_str); + wav_path= gDirUtilp->getExpandedFilename(LL_PATH_CACHE,uuid_str) + ".dsf"; + + if (!mBufferp->loadWAV(wav_path)) + { + // Hrm. Right now, let's unset the buffer, since it's empty. + gAudiop->cleanupBuffer(mBufferp); + mBufferp = NULL; + + return false; + } + mBufferp->mAudioDatap = this; + return true; +} + + diff --git a/indra/llaudio/llaudioengine.h b/indra/llaudio/llaudioengine.h new file mode 100644 index 0000000000..457fd93abe --- /dev/null +++ b/indra/llaudio/llaudioengine.h @@ -0,0 +1,452 @@ +/** + * @file audioengine.h + * @brief Definition of LLAudioEngine base class abstracting the audio support + * + * $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_AUDIOENGINE_H +#define LL_AUDIOENGINE_H + +#include <list> +#include <map> + +#include "v3math.h" +#include "v3dmath.h" +#include "lltimer.h" +#include "lluuid.h" +#include "llframetimer.h" +#include "llassettype.h" + +#include "lllistener.h" + +const F32 LL_WIND_UPDATE_INTERVAL = 0.1f; +const F32 LL_ROLLOFF_MULTIPLIER_UNDER_WATER = 5.f; // How much sounds are weaker under water +const F32 LL_WIND_UNDERWATER_CENTER_FREQ = 20.f; + +const F32 ATTACHED_OBJECT_TIMEOUT = 5.0f; +const F32 DEFAULT_MIN_DISTANCE = 2.0f; + +#define MAX_CHANNELS 30 +#define MAX_BUFFERS 40 // Some extra for preloading, maybe? + +// This define is intended to allow us to switch from os based wav +// file loading to vfs based wav file loading. The problem is that I +// am unconvinced that the LLWaveFile works for loading sounds from +// memory. So, until that is fixed up, changed, whatever, this remains +// undefined. +//#define USE_WAV_VFILE + +class LLVFS; + +class LLAudioSource; +class LLAudioData; +class LLAudioChannel; +class LLAudioChannelOpenAL; +class LLAudioBuffer; +class LLStreamingAudioInterface; + + +// +// LLAudioEngine definition +// + +class LLAudioEngine +{ + friend class LLAudioChannelOpenAL; // bleh. channel needs some listener methods. + +public: + enum LLAudioType + { + AUDIO_TYPE_NONE = 0, + AUDIO_TYPE_SFX = 1, + AUDIO_TYPE_UI = 2, + AUDIO_TYPE_AMBIENT = 3, + AUDIO_TYPE_COUNT = 4 // last + }; + + LLAudioEngine(); + virtual ~LLAudioEngine(); + + // initialization/startup/shutdown + virtual bool init(const S32 num_channels, void *userdata); + virtual std::string getDriverName(bool verbose) = 0; + virtual void shutdown(); + + // Used by the mechanics of the engine + //virtual void processQueue(const LLUUID &sound_guid); + virtual void setListener(LLVector3 pos,LLVector3 vel,LLVector3 up,LLVector3 at); + virtual void updateWind(LLVector3 direction, F32 camera_height_above_water) = 0; + virtual void idle(F32 max_decode_time = 0.f); + virtual void updateChannels(); + + // + // "End user" functionality + // + virtual bool isWindEnabled(); + virtual void enableWind(bool state_b); + + // Use these for temporarily muting the audio system. + // Does not change buffers, initialization, etc. but + // stops playing new sounds. + virtual void setMuted(bool muted); + virtual bool getMuted() const { return mMuted; } +#ifdef USE_PLUGIN_MEDIA + LLPluginClassMedia* initializeMedia(const std::string& media_type); +#endif + F32 getMasterGain(); + void setMasterGain(F32 gain); + + F32 getSecondaryGain(S32 type); + void setSecondaryGain(S32 type, F32 gain); + + F32 getInternetStreamGain(); + + virtual void setDopplerFactor(F32 factor); + virtual F32 getDopplerFactor(); + virtual void setRolloffFactor(F32 factor); + virtual F32 getRolloffFactor(); + virtual void setMaxWindGain(F32 gain); + + + // Methods actually related to setting up and removing sounds + // Owner ID is the owner of the object making the request + void triggerSound(const LLUUID &sound_id, const LLUUID& owner_id, const F32 gain, + const S32 type = LLAudioEngine::AUDIO_TYPE_NONE, + const LLVector3d &pos_global = LLVector3d::zero); + bool preloadSound(const LLUUID &id); + + void addAudioSource(LLAudioSource *asp); + void cleanupAudioSource(LLAudioSource *asp); + + LLAudioSource *findAudioSource(const LLUUID &source_id); + LLAudioData *getAudioData(const LLUUID &audio_uuid); + + // Internet stream implementation manipulation + LLStreamingAudioInterface *getStreamingAudioImpl(); + void setStreamingAudioImpl(LLStreamingAudioInterface *impl); + // Internet stream methods - these will call down into the *mStreamingAudioImpl if it exists + void startInternetStream(const std::string& url); + void stopInternetStream(); + void pauseInternetStream(int pause); + void updateInternetStream(); // expected to be called often + int isInternetStreamPlaying(); + // use a value from 0.0 to 1.0, inclusive + void setInternetStreamGain(F32 vol); + std::string getInternetStreamURL(); + + // For debugging usage + virtual LLVector3 getListenerPos(); + + LLAudioBuffer *getFreeBuffer(); // Get a free buffer, or flush an existing one if you have to. + LLAudioChannel *getFreeChannel(const F32 priority); // Get a free channel or flush an existing one if your priority is higher + void cleanupBuffer(LLAudioBuffer *bufferp); + + bool hasDecodedFile(const LLUUID &uuid); + bool hasLocalFile(const LLUUID &uuid); + + bool updateBufferForData(LLAudioData *adp, const LLUUID &audio_uuid = LLUUID::null); + + + // Asset callback when we're retrieved a sound from the asset server. + void startNextTransfer(); + static void assetCallback(LLVFS *vfs, const LLUUID &uuid, LLAssetType::EType type, void *user_data, S32 result_code, LLExtStat ext_status); + + friend class LLPipeline; // For debugging +public: + F32 mMaxWindGain; // Hack. Public to set before fade in? + +protected: + virtual LLAudioBuffer *createBuffer() = 0; + virtual LLAudioChannel *createChannel() = 0; + + virtual void initWind() = 0; + virtual void cleanupWind() = 0; + virtual void setInternalGain(F32 gain) = 0; + + void commitDeferredChanges(); + + virtual void allocateListener() = 0; + + + // listener methods + virtual void setListenerPos(LLVector3 vec); + virtual void setListenerVelocity(LLVector3 vec); + virtual void orientListener(LLVector3 up, LLVector3 at); + virtual void translateListener(LLVector3 vec); + + + F64 mapWindVecToGain(LLVector3 wind_vec); + F64 mapWindVecToPitch(LLVector3 wind_vec); + F64 mapWindVecToPan(LLVector3 wind_vec); + +protected: + LLListener *mListenerp; + + bool mMuted; + void* mUserData; + + S32 mLastStatus; + + S32 mNumChannels; + bool mEnableWind; + + LLUUID mCurrentTransfer; // Audio file currently being transferred by the system + LLFrameTimer mCurrentTransferTimer; + + // A list of all audio sources that are known to the viewer at this time. + // This is most likely a superset of the ones that we actually have audio + // data for, or are playing back. + typedef std::map<LLUUID, LLAudioSource *> source_map; + typedef std::map<LLUUID, LLAudioData *> data_map; + + source_map mAllSources; + data_map mAllData; + + LLAudioChannel *mChannels[MAX_CHANNELS]; + + // Buffers needs to change into a different data structure, as the number of buffers + // that we have active should be limited by RAM usage, not count. + LLAudioBuffer *mBuffers[MAX_BUFFERS]; + + F32 mMasterGain; + F32 mSecondaryGain[AUDIO_TYPE_COUNT]; + + F32 mNextWindUpdate; + + LLFrameTimer mWindUpdateTimer; + +private: + void setDefaults(); + LLStreamingAudioInterface *mStreamingAudioImpl; +}; + + + + +// +// Standard audio source. Can be derived from for special sources, such as those attached to objects. +// + + +class LLAudioSource +{ +public: + // owner_id is the id of the agent responsible for making this sound + // play, for example, the owner of the object currently playing it + LLAudioSource(const LLUUID &id, const LLUUID& owner_id, const F32 gain, const S32 type = LLAudioEngine::AUDIO_TYPE_NONE); + virtual ~LLAudioSource(); + + virtual void update(); // Update this audio source + void updatePriority(); + + void preload(const LLUUID &audio_id); // Only used for preloading UI sounds, now. + + void addAudioData(LLAudioData *adp, bool set_current = TRUE); + + void setAmbient(const bool ambient) { mAmbient = ambient; } + bool isAmbient() const { return mAmbient; } + + void setLoop(const bool loop) { mLoop = loop; } + bool isLoop() const { return mLoop; } + + void setSyncMaster(const bool master) { mSyncMaster = master; } + bool isSyncMaster() const { return mSyncMaster; } + + void setSyncSlave(const bool slave) { mSyncSlave = slave; } + bool isSyncSlave() const { return mSyncSlave; } + + void setQueueSounds(const bool queue) { mQueueSounds = queue; } + bool isQueueSounds() const { return mQueueSounds; } + + void setPlayedOnce(const bool played_once) { mPlayedOnce = played_once; } + + void setType(S32 type) { mType = type; } + S32 getType() { return mType; } + + void setPositionGlobal(const LLVector3d &position_global) { mPositionGlobal = position_global; } + LLVector3d getPositionGlobal() const { return mPositionGlobal; } + LLVector3 getVelocity() const { return mVelocity; } + F32 getPriority() const { return mPriority; } + + // Gain should always be clamped between 0 and 1. + F32 getGain() const { return mGain; } + virtual void setGain(const F32 gain) { mGain = llclamp(gain, 0.f, 1.f); } + + const LLUUID &getID() const { return mID; } + bool isDone(); + + LLAudioData *getCurrentData(); + LLAudioData *getQueuedData(); + LLAudioBuffer *getCurrentBuffer(); + + bool setupChannel(); + bool play(const LLUUID &audio_id); // Start the audio source playing + + bool hasPendingPreloads() const; // Has preloads that haven't been done yet + + friend class LLAudioEngine; + friend class LLAudioChannel; +protected: + void setChannel(LLAudioChannel *channelp); + LLAudioChannel *getChannel() const { return mChannelp; } + +protected: + LLUUID mID; // The ID of the source is that of the object if it's attached to an object. + LLUUID mOwnerID; // owner of the object playing the sound + F32 mPriority; + F32 mGain; + bool mAmbient; + bool mLoop; + bool mSyncMaster; + bool mSyncSlave; + bool mQueueSounds; + bool mPlayedOnce; + S32 mType; + LLVector3d mPositionGlobal; + LLVector3 mVelocity; + + //LLAudioSource *mSyncMasterp; // If we're a slave, the source that we're synced to. + LLAudioChannel *mChannelp; // If we're currently playing back, this is the channel that we're assigned to. + LLAudioData *mCurrentDatap; + LLAudioData *mQueuedDatap; + + typedef std::map<LLUUID, LLAudioData *> data_map; + data_map mPreloadMap; + + LLFrameTimer mAgeTimer; +}; + + + + +// +// Generic metadata about a particular piece of audio data. +// The actual data is handled by the derived LLAudioBuffer classes which are +// derived for each audio engine. +// + + +class LLAudioData +{ +public: + LLAudioData(const LLUUID &uuid); + bool load(); + + LLUUID getID() const { return mID; } + LLAudioBuffer *getBuffer() const { return mBufferp; } + + bool hasLocalData() const { return mHasLocalData; } + bool hasDecodedData() const { return mHasDecodedData; } + bool hasValidData() const { return mHasValidData; } + + void setHasLocalData(const bool hld) { mHasLocalData = hld; } + void setHasDecodedData(const bool hdd) { mHasDecodedData = hdd; } + void setHasValidData(const bool hvd) { mHasValidData = hvd; } + + friend class LLAudioEngine; // Severe laziness, bad. + +protected: + LLUUID mID; + LLAudioBuffer *mBufferp; // If this data is being used by the audio system, a pointer to the buffer will be set here. + bool mHasLocalData; + bool mHasDecodedData; + bool mHasValidData; +}; + + +// +// Base class for an audio channel, i.e. a channel which is capable of playing back a sound. +// Management of channels is done generically, methods for actually manipulating the channel +// are derived for each audio engine. +// + + +class LLAudioChannel +{ +public: + LLAudioChannel(); + virtual ~LLAudioChannel(); + + virtual void setSource(LLAudioSource *sourcep); + LLAudioSource *getSource() const { return mCurrentSourcep; } + + void setSecondaryGain(F32 gain) { mSecondaryGain = gain; } + F32 getSecondaryGain() { return mSecondaryGain; } + + friend class LLAudioEngine; + friend class LLAudioSource; +protected: + virtual void play() = 0; + virtual void playSynced(LLAudioChannel *channelp) = 0; + virtual void cleanup() = 0; + virtual bool isPlaying() = 0; + void setWaiting(const bool waiting) { mWaiting = waiting; } + bool isWaiting() const { return mWaiting; } + + virtual bool updateBuffer(); // Check to see if the buffer associated with the source changed, and update if necessary. + virtual void update3DPosition() = 0; + virtual void updateLoop() = 0; // Update your loop/completion status, for use by queueing/syncing. +protected: + LLAudioSource *mCurrentSourcep; + LLAudioBuffer *mCurrentBufferp; + bool mLoopedThisFrame; + bool mWaiting; // Waiting for sync. + F32 mSecondaryGain; +}; + + + + +// Basically an interface class to the engine-specific implementation +// of audio data that's ready for playback. +// Will likely get more complex as we decide to do stuff like real streaming audio. + + +class LLAudioBuffer +{ +public: + virtual ~LLAudioBuffer() {}; + virtual bool loadWAV(const std::string& filename) = 0; + virtual U32 getLength() = 0; + + friend class LLAudioEngine; + friend class LLAudioChannel; + friend class LLAudioData; +protected: + bool mInUse; + LLAudioData *mAudioDatap; + LLFrameTimer mLastUseTimer; +}; + + + +extern LLAudioEngine* gAudiop; + +#endif diff --git a/indra/llaudio/llaudioengine_fmod.cpp b/indra/llaudio/llaudioengine_fmod.cpp new file mode 100644 index 0000000000..7b12b62d53 --- /dev/null +++ b/indra/llaudio/llaudioengine_fmod.cpp @@ -0,0 +1,766 @@ +/** + * @file audioengine_fmod.cpp + * @brief Implementation of LLAudioEngine class abstracting the audio support as a FMOD 3D implementation + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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 "llstreamingaudio.h" +#include "llstreamingaudio_fmod.h" + +#include "llaudioengine_fmod.h" +#include "lllistener_fmod.h" + +#include "llerror.h" +#include "llmath.h" +#include "llrand.h" + +#include "fmod.h" +#include "fmod_errors.h" +#include "lldir.h" +#include "llapr.h" + +#include "sound_ids.h" + + +extern "C" { + void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata); +} + +FSOUND_DSPUNIT *gWindDSP = NULL; + + +LLAudioEngine_FMOD::LLAudioEngine_FMOD() +{ + mInited = false; + mWindGen = NULL; +} + + +LLAudioEngine_FMOD::~LLAudioEngine_FMOD() +{ +} + + +bool LLAudioEngine_FMOD::init(const S32 num_channels, void* userdata) +{ + LLAudioEngine::init(num_channels, userdata); + + // Reserve one extra channel for the http stream. + if (!FSOUND_SetMinHardwareChannels(num_channels + 1)) + { + LL_WARNS("AppInit") << "FMOD::init[0](), error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + } + + LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() initializing FMOD" << LL_ENDL; + + F32 version = FSOUND_GetVersion(); + if (version < FMOD_VERSION) + { + LL_WARNS("AppInit") << "Error : You are using the wrong FMOD version (" << version + << ")! You should be using FMOD " << FMOD_VERSION << LL_ENDL; + //return false; + } + + U32 fmod_flags = 0x0; + +#if LL_WINDOWS + // Windows needs to know which window is frontmost. + // This must be called before FSOUND_Init() per the FMOD docs. + // This could be used to let FMOD handle muting when we lose focus, + // but we don't actually want to do that because we want to distinguish + // between minimized and not-focused states. + if (!FSOUND_SetHWND(userdata)) + { + LL_WARNS("AppInit") << "Error setting FMOD window: " + << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + return false; + } + // Play audio when we don't have focus. + // (For example, IM client on top of us.) + // This means we also try to play audio when minimized, + // so we manually handle muting in that case. JC + fmod_flags |= FSOUND_INIT_GLOBALFOCUS; +#endif + +#if LL_LINUX + // initialize the FMOD engine + + // This is a hack to use only FMOD's basic FPU mixer + // when the LL_VALGRIND environmental variable is set, + // otherwise valgrind will fall over on FMOD's MMX detection + if (getenv("LL_VALGRIND")) /*Flawfinder: ignore*/ + { + LL_INFOS("AppInit") << "Pacifying valgrind in FMOD init." << LL_ENDL; + FSOUND_SetMixer(FSOUND_MIXER_QUALITY_FPU); + } + + // If we don't set an output method, Linux FMOD always + // decides on OSS and fails otherwise. So we'll manually + // try ESD, then OSS, then ALSA. + // Why this order? See SL-13250, but in short, OSS emulated + // on top of ALSA is ironically more reliable than raw ALSA. + // Ack, and ESD has more reliable failure modes - but has worse + // latency - than all of them, so wins for now. + bool audio_ok = false; + + if (!audio_ok) + if (NULL == getenv("LL_BAD_FMOD_ESD")) /*Flawfinder: ignore*/ + { + LL_DEBUGS("AppInit") << "Trying ESD audio output..." << LL_ENDL; + if(FSOUND_SetOutput(FSOUND_OUTPUT_ESD) && + FSOUND_Init(44100, num_channels, fmod_flags)) + { + LL_DEBUGS("AppInit") << "ESD audio output initialized OKAY" + << LL_ENDL; + audio_ok = true; + } else { + LL_WARNS("AppInit") << "ESD audio output FAILED to initialize: " + << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + } + } else { + LL_DEBUGS("AppInit") << "ESD audio output SKIPPED" << LL_ENDL; + } + + if (!audio_ok) + if (NULL == getenv("LL_BAD_FMOD_OSS")) /*Flawfinder: ignore*/ + { + LL_DEBUGS("AppInit") << "Trying OSS audio output..." << LL_ENDL; + if(FSOUND_SetOutput(FSOUND_OUTPUT_OSS) && + FSOUND_Init(44100, num_channels, fmod_flags)) + { + LL_DEBUGS("AppInit") << "OSS audio output initialized OKAY" << LL_ENDL; + audio_ok = true; + } else { + LL_WARNS("AppInit") << "OSS audio output FAILED to initialize: " + << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + } + } else { + LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; + } + + if (!audio_ok) + if (NULL == getenv("LL_BAD_FMOD_ALSA")) /*Flawfinder: ignore*/ + { + LL_DEBUGS("AppInit") << "Trying ALSA audio output..." << LL_ENDL; + if(FSOUND_SetOutput(FSOUND_OUTPUT_ALSA) && + FSOUND_Init(44100, num_channels, fmod_flags)) + { + LL_DEBUGS("AppInit") << "ALSA audio output initialized OKAY" << LL_ENDL; + audio_ok = true; + } else { + LL_WARNS("AppInit") << "ALSA audio output FAILED to initialize: " + << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + } + } else { + LL_DEBUGS("AppInit") << "OSS audio output SKIPPED" << LL_ENDL; + } + + if (!audio_ok) + { + LL_WARNS("AppInit") << "Overall audio init failure." << LL_ENDL; + return false; + } + + // On Linux, FMOD causes a SIGPIPE for some netstream error + // conditions (an FMOD bug); ignore SIGPIPE so it doesn't crash us. + // NOW FIXED in FMOD 3.x since 2006-10-01. + //signal(SIGPIPE, SIG_IGN); + + // We're interested in logging which output method we + // ended up with, for QA purposes. + switch (FSOUND_GetOutput()) + { + case FSOUND_OUTPUT_NOSOUND: LL_DEBUGS("AppInit") << "Audio output: NoSound" << LL_ENDL; break; + case FSOUND_OUTPUT_OSS: LL_DEBUGS("AppInit") << "Audio output: OSS" << LL_ENDL; break; + case FSOUND_OUTPUT_ESD: LL_DEBUGS("AppInit") << "Audio output: ESD" << LL_ENDL; break; + case FSOUND_OUTPUT_ALSA: LL_DEBUGS("AppInit") << "Audio output: ALSA" << LL_ENDL; break; + default: LL_INFOS("AppInit") << "Audio output: Unknown!" << LL_ENDL; break; + }; + +#else // LL_LINUX + + // initialize the FMOD engine + if (!FSOUND_Init(44100, num_channels, fmod_flags)) + { + LL_WARNS("AppInit") << "Error initializing FMOD: " + << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + return false; + } + +#endif + + // set up our favourite FMOD-native streaming audio implementation if none has already been added + if (!getStreamingAudioImpl()) // no existing implementation added + setStreamingAudioImpl(new LLStreamingAudio_FMOD()); + + LL_DEBUGS("AppInit") << "LLAudioEngine_FMOD::init() FMOD initialized correctly" << LL_ENDL; + + mInited = true; + + return true; +} + + +std::string LLAudioEngine_FMOD::getDriverName(bool verbose) +{ + if (verbose) + { + F32 version = FSOUND_GetVersion(); + return llformat("FMOD version %f", version); + } + else + { + return "FMOD"; + } +} + + +void LLAudioEngine_FMOD::allocateListener(void) +{ + mListenerp = (LLListener *) new LLListener_FMOD(); + if (!mListenerp) + { + llwarns << "Listener creation failed" << llendl; + } +} + + +void LLAudioEngine_FMOD::shutdown() +{ + if (gWindDSP) + { + FSOUND_DSP_SetActive(gWindDSP,false); + FSOUND_DSP_Free(gWindDSP); + } + + stopInternetStream(); + + LLAudioEngine::shutdown(); + + llinfos << "LLAudioEngine_FMOD::shutdown() closing FMOD" << llendl; + FSOUND_Close(); + llinfos << "LLAudioEngine_FMOD::shutdown() done closing FMOD" << llendl; + + delete mListenerp; + mListenerp = NULL; +} + + +LLAudioBuffer * LLAudioEngine_FMOD::createBuffer() +{ + return new LLAudioBufferFMOD(); +} + + +LLAudioChannel * LLAudioEngine_FMOD::createChannel() +{ + return new LLAudioChannelFMOD(); +} + + +void LLAudioEngine_FMOD::initWind() +{ + mWindGen = new LLWindGen<MIXBUFFERFORMAT>; + + if (!gWindDSP) + { + gWindDSP = FSOUND_DSP_Create(&windCallback, FSOUND_DSP_DEFAULTPRIORITY_CLEARUNIT + 20, mWindGen); + } + if (gWindDSP) + { + FSOUND_DSP_SetActive(gWindDSP, true); + } + mNextWindUpdate = 0.0; +} + + +void LLAudioEngine_FMOD::cleanupWind() +{ + if (gWindDSP) + { + FSOUND_DSP_SetActive(gWindDSP, false); + FSOUND_DSP_Free(gWindDSP); + gWindDSP = NULL; + } + + delete mWindGen; + mWindGen = NULL; +} + + +//----------------------------------------------------------------------- +void LLAudioEngine_FMOD::updateWind(LLVector3 wind_vec, F32 camera_height_above_water) +{ + LLVector3 wind_pos; + F64 pitch; + F64 center_freq; + + if (!mEnableWind) + { + return; + } + + if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL)) + { + + // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up) + // need to convert this to the conventional orientation DS3D and OpenAL use + // where +X = right, +Y = up, +Z = backwards + + wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]); + + // cerr << "Wind update" << endl; + + pitch = 1.0 + mapWindVecToPitch(wind_vec); + center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0)); + + mWindGen->mTargetFreq = (F32)center_freq; + mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain; + mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec); + } +} + +/* +//----------------------------------------------------------------------- +void LLAudioEngine_FMOD::setSourceMinDistance(U16 source_num, F64 distance) +{ + if (!mInited) + { + return; + } + if (mBuffer[source_num]) + { + mMinDistance[source_num] = (F32) distance; + if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num])) + { + llwarns << "FMOD::setSourceMinDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + } +} + +//----------------------------------------------------------------------- +void LLAudioEngine_FMOD::setSourceMaxDistance(U16 source_num, F64 distance) +{ + if (!mInited) + { + return; + } + if (mBuffer[source_num]) + { + mMaxDistance[source_num] = (F32) distance; + if (!FSOUND_Sample_SetMinMaxDistance(mBuffer[source_num],mMinDistance[source_num], mMaxDistance[source_num])) + { + llwarns << "FMOD::setSourceMaxDistance(" << source_num << "), error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + } +} + +//----------------------------------------------------------------------- +void LLAudioEngine_FMOD::get3DParams(S32 source_num, S32 *volume, S32 *freq, S32 *inside, S32 *outside, LLVector3 *orient, S32 *out_volume, F32 *min_dist, F32 *max_dist) +{ + *volume = 0; + *freq = 0; + *inside = 0; + *outside = 0; + *orient = LLVector3::zero; + *out_volume = 0; + *min_dist = 0.f; + *max_dist = 0.f; +} + +*/ + + +//----------------------------------------------------------------------- +void LLAudioEngine_FMOD::setInternalGain(F32 gain) +{ + if (!mInited) + { + return; + } + + gain = llclamp( gain, 0.0f, 1.0f ); + FSOUND_SetSFXMasterVolume( llround( 255.0f * gain ) ); + + LLStreamingAudioInterface *saimpl = getStreamingAudioImpl(); + if ( saimpl ) + { + // fmod likes its streaming audio channel gain re-asserted after + // master volume change. + saimpl->setGain(saimpl->getGain()); + } +} + +// +// LLAudioChannelFMOD implementation +// + +LLAudioChannelFMOD::LLAudioChannelFMOD() : LLAudioChannel(), mChannelID(0), mLastSamplePos(0) +{ +} + + +LLAudioChannelFMOD::~LLAudioChannelFMOD() +{ + cleanup(); +} + + +bool LLAudioChannelFMOD::updateBuffer() +{ + if (LLAudioChannel::updateBuffer()) + { + // Base class update returned true, which means that we need to actually + // set up the channel for a different buffer. + + LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentSourcep->getCurrentBuffer(); + + // Grab the FMOD sample associated with the buffer + FSOUND_SAMPLE *samplep = bufferp->getSample(); + if (!samplep) + { + // This is bad, there should ALWAYS be a sample associated with a legit + // buffer. + llerrs << "No FMOD sample!" << llendl; + return false; + } + + + // Actually play the sound. Start it off paused so we can do all the necessary + // setup. + mChannelID = FSOUND_PlaySoundEx(FSOUND_FREE, samplep, FSOUND_DSP_GetSFXUnit(), true); + + //llinfos << "Setting up channel " << std::hex << mChannelID << std::dec << llendl; + } + + // If we have a source for the channel, we need to update its gain. + if (mCurrentSourcep) + { + // SJB: warnings can spam and hurt framerate, disabling + if (!FSOUND_SetVolume(mChannelID, llround(getSecondaryGain() * mCurrentSourcep->getGain() * 255.0f))) + { +// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + + if (!FSOUND_SetLoopMode(mChannelID, mCurrentSourcep->isLoop() ? FSOUND_LOOP_NORMAL : FSOUND_LOOP_OFF)) + { +// llwarns << "Channel " << mChannelID << "Source ID: " << mCurrentSourcep->getID() +// << " at " << mCurrentSourcep->getPositionGlobal() << llendl; +// llwarns << "LLAudioChannelFMOD::updateBuffer error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + } + + return true; +} + + +void LLAudioChannelFMOD::update3DPosition() +{ + if (!mChannelID) + { + // We're not actually a live channel (i.e., we're not playing back anything) + return; + } + + LLAudioBufferFMOD *bufferp = (LLAudioBufferFMOD *)mCurrentBufferp; + if (!bufferp) + { + // We don't have a buffer associated with us (should really have been picked up + // by the above if. + return; + } + + if (mCurrentSourcep->isAmbient()) + { + // Ambient sound, don't need to do any positional updates. + bufferp->set3DMode(false); + } + else + { + // Localized sound. Update the position and velocity of the sound. + bufferp->set3DMode(true); + + LLVector3 float_pos; + float_pos.setVec(mCurrentSourcep->getPositionGlobal()); + if (!FSOUND_3D_SetAttributes(mChannelID, float_pos.mV, mCurrentSourcep->getVelocity().mV)) + { + LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::update3DPosition error: " << FMOD_ErrorString(FSOUND_GetError()) << LL_ENDL; + } + } +} + + +void LLAudioChannelFMOD::updateLoop() +{ + if (!mChannelID) + { + // May want to clear up the loop/sample counters. + return; + } + + // + // Hack: We keep track of whether we looped or not by seeing when the + // sample position looks like it's going backwards. Not reliable; may + // yield false negatives. + // + U32 cur_pos = FSOUND_GetCurrentPosition(mChannelID); + if (cur_pos < (U32)mLastSamplePos) + { + mLoopedThisFrame = true; + } + mLastSamplePos = cur_pos; +} + + +void LLAudioChannelFMOD::cleanup() +{ + if (!mChannelID) + { + //llinfos << "Aborting cleanup with no channelID." << llendl; + return; + } + + //llinfos << "Cleaning up channel: " << mChannelID << llendl; + if (!FSOUND_StopSound(mChannelID)) + { + LL_DEBUGS("FMOD") << "LLAudioChannelFMOD::cleanup error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + + mCurrentBufferp = NULL; + mChannelID = 0; +} + + +void LLAudioChannelFMOD::play() +{ + if (!mChannelID) + { + llwarns << "Playing without a channelID, aborting" << llendl; + return; + } + + if (!FSOUND_SetPaused(mChannelID, false)) + { + llwarns << "LLAudioChannelFMOD::play error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + getSource()->setPlayedOnce(true); +} + + +void LLAudioChannelFMOD::playSynced(LLAudioChannel *channelp) +{ + LLAudioChannelFMOD *fmod_channelp = (LLAudioChannelFMOD*)channelp; + if (!(fmod_channelp->mChannelID && mChannelID)) + { + // Don't have channels allocated to both the master and the slave + return; + } + + U32 position = FSOUND_GetCurrentPosition(fmod_channelp->mChannelID) % mCurrentBufferp->getLength(); + // Try to match the position of our sync master + if (!FSOUND_SetCurrentPosition(mChannelID, position)) + { + llwarns << "LLAudioChannelFMOD::playSynced unable to set current position" << llendl; + } + + // Start us playing + play(); +} + + +bool LLAudioChannelFMOD::isPlaying() +{ + if (!mChannelID) + { + return false; + } + + return FSOUND_IsPlaying(mChannelID) && (!FSOUND_GetPaused(mChannelID)); +} + + + +// +// LLAudioBufferFMOD implementation +// + + +LLAudioBufferFMOD::LLAudioBufferFMOD() +{ + mSamplep = NULL; +} + + +LLAudioBufferFMOD::~LLAudioBufferFMOD() +{ + if (mSamplep) + { + // Clean up the associated FMOD sample if it exists. + FSOUND_Sample_Free(mSamplep); + mSamplep = NULL; + } +} + + +bool LLAudioBufferFMOD::loadWAV(const std::string& filename) +{ + // Try to open a wav file from disk. This will eventually go away, as we don't + // really want to block doing this. + if (filename.empty()) + { + // invalid filename, abort. + return false; + } + + if (!LLAPRFile::isExist(filename, NULL, LL_APR_RPB)) + { + // File not found, abort. + return false; + } + + if (mSamplep) + { + // If there's already something loaded in this buffer, clean it up. + FSOUND_Sample_Free(mSamplep); + mSamplep = NULL; + } + + // Load up the wav file into an fmod sample +#if LL_WINDOWS + // MikeS. - Loading the sound file manually and then handing it over to FMOD, + // since FMOD uses posix IO internally, + // which doesn't work with unicode file paths. + LLFILE* sound_file = LLFile::fopen(filename,"rb"); /* Flawfinder: ignore */ + if (sound_file) + { + fseek(sound_file,0,SEEK_END); + U32 file_length = ftell(sound_file); //Find the length of the file by seeking to the end and getting the offset + size_t read_count; + fseek(sound_file,0,SEEK_SET); //Seek back to the beginning + char* buffer = new char[file_length]; + llassert(buffer); + read_count = fread((void*)buffer,file_length,1,sound_file);//Load it.. + if(ferror(sound_file)==0 && (read_count == 1)){//No read error, and we got 1 chunk of our size... + unsigned int mode_flags = FSOUND_LOOP_NORMAL | FSOUND_LOADMEMORY; + //FSOUND_16BITS | FSOUND_MONO | FSOUND_LOADMEMORY | FSOUND_LOOP_NORMAL; + mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, buffer, mode_flags , 0, file_length); + } + delete[] buffer; + fclose(sound_file); + } +#else + mSamplep = FSOUND_Sample_Load(FSOUND_UNMANAGED, filename.c_str(), FSOUND_LOOP_NORMAL, 0, 0); +#endif + + if (!mSamplep) + { + // We failed to load the file for some reason. + llwarns << "Could not load data '" << filename << "': " + << FMOD_ErrorString(FSOUND_GetError()) << llendl; + + // + // If we EVER want to load wav files provided by end users, we need + // to rethink this! + // + // file is probably corrupt - remove it. + LLFile::remove(filename); + return false; + } + + // Everything went well, return true + return true; +} + + +U32 LLAudioBufferFMOD::getLength() +{ + if (!mSamplep) + { + return 0; + } + + return FSOUND_Sample_GetLength(mSamplep); +} + + +void LLAudioBufferFMOD::set3DMode(bool use3d) +{ + U16 current_mode = FSOUND_Sample_GetMode(mSamplep); + + if (use3d) + { + if (!FSOUND_Sample_SetMode(mSamplep, (current_mode & (~FSOUND_2D)))) + { + llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + } + else + { + if (!FSOUND_Sample_SetMode(mSamplep, current_mode | FSOUND_2D)) + { + llwarns << "LLAudioBufferFMOD::set3DMode error: " << FMOD_ErrorString(FSOUND_GetError()) << llendl; + } + } +} + + +void * F_CALLBACKAPI windCallback(void *originalbuffer, void *newbuffer, int length, void* userdata) +{ + // originalbuffer = fmod's original mixbuffer. + // newbuffer = the buffer passed from the previous DSP unit. + // length = length in samples at this mix time. + // param = user parameter passed through in FSOUND_DSP_Create. + // + // modify the buffer in some fashion + + LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *windgen = + (LLWindGen<LLAudioEngine_FMOD::MIXBUFFERFORMAT> *)userdata; + U8 stride; + +#if LL_DARWIN + stride = sizeof(LLAudioEngine_FMOD::MIXBUFFERFORMAT); +#else + int mixertype = FSOUND_GetMixer(); + if (mixertype == FSOUND_MIXER_BLENDMODE || + mixertype == FSOUND_MIXER_QUALITY_FPU) + { + stride = 4; + } + else + { + stride = 2; + } +#endif + + newbuffer = windgen->windGenerate((LLAudioEngine_FMOD::MIXBUFFERFORMAT *)newbuffer, length, stride); + + return newbuffer; +} diff --git a/indra/llaudio/llaudioengine_fmod.h b/indra/llaudio/llaudioengine_fmod.h new file mode 100644 index 0000000000..3968657cba --- /dev/null +++ b/indra/llaudio/llaudioengine_fmod.h @@ -0,0 +1,129 @@ +/** + * @file audioengine_fmod.h + * @brief Definition of LLAudioEngine class abstracting the audio + * support as a FMOD 3D implementation + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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_AUDIOENGINE_FMOD_H +#define LL_AUDIOENGINE_FMOD_H + +#include "llaudioengine.h" +#include "lllistener_fmod.h" +#include "llwindgen.h" + +#include "fmod.h" + +class LLAudioStreamManagerFMOD; + +class LLAudioEngine_FMOD : public LLAudioEngine +{ +public: + LLAudioEngine_FMOD(); + virtual ~LLAudioEngine_FMOD(); + + // initialization/startup/shutdown + virtual bool init(const S32 num_channels, void *user_data); + virtual std::string getDriverName(bool verbose); + virtual void allocateListener(); + + virtual void shutdown(); + + /*virtual*/ void initWind(); + /*virtual*/ void cleanupWind(); + + /*virtual*/void updateWind(LLVector3 direction, F32 camera_height_above_water); + +#if LL_DARWIN + typedef S32 MIXBUFFERFORMAT; +#else + typedef S16 MIXBUFFERFORMAT; +#endif + +protected: + /*virtual*/ LLAudioBuffer *createBuffer(); // Get a free buffer, or flush an existing one if you have to. + /*virtual*/ LLAudioChannel *createChannel(); // Create a new audio channel. + + /*virtual*/ void setInternalGain(F32 gain); +protected: + static signed char F_CALLBACKAPI callbackMetaData(char* name, char* value, void* userdata); + + //F32 mMinDistance[MAX_BUFFERS]; + //F32 mMaxDistance[MAX_BUFFERS]; + + bool mInited; + + // On Windows, userdata is the HWND of the application window. + void* mUserData; + + LLWindGen<MIXBUFFERFORMAT> *mWindGen; +}; + + +class LLAudioChannelFMOD : public LLAudioChannel +{ +public: + LLAudioChannelFMOD(); + virtual ~LLAudioChannelFMOD(); + +protected: + /*virtual*/ void play(); + /*virtual*/ void playSynced(LLAudioChannel *channelp); + /*virtual*/ void cleanup(); + /*virtual*/ bool isPlaying(); + + /*virtual*/ bool updateBuffer(); + /*virtual*/ void update3DPosition(); + /*virtual*/ void updateLoop(); + +protected: + int mChannelID; + S32 mLastSamplePos; +}; + + +class LLAudioBufferFMOD : public LLAudioBuffer +{ +public: + LLAudioBufferFMOD(); + virtual ~LLAudioBufferFMOD(); + + /*virtual*/ bool loadWAV(const std::string& filename); + /*virtual*/ U32 getLength(); + friend class LLAudioChannelFMOD; + + void set3DMode(bool use3d); +protected: + FSOUND_SAMPLE *getSample() { return mSamplep; } +protected: + FSOUND_SAMPLE *mSamplep; +}; + + +#endif // LL_AUDIOENGINE_FMOD_H diff --git a/indra/llaudio/llaudioengine_openal.cpp b/indra/llaudio/llaudioengine_openal.cpp new file mode 100644 index 0000000000..a5982ccbd6 --- /dev/null +++ b/indra/llaudio/llaudioengine_openal.cpp @@ -0,0 +1,546 @@ +/** + * @file audioengine_openal.cpp + * @brief implementation of audio engine using OpenAL + * support as a OpenAL 3D implementation + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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 "lldir.h" + +#include "llaudioengine_openal.h" +#include "lllistener_openal.h" + + +LLAudioEngine_OpenAL::LLAudioEngine_OpenAL() + : + mWindGen(NULL), + mWindBuf(NULL), + mWindBufFreq(0), + mWindBufSamples(0), + mWindBufBytes(0), + mWindSource(AL_NONE), + mNumEmptyWindALBuffers(MAX_NUM_WIND_BUFFERS) +{ +} + +// virtual +LLAudioEngine_OpenAL::~LLAudioEngine_OpenAL() +{ +} + +// virtual +bool LLAudioEngine_OpenAL::init(const S32 num_channels, void* userdata) +{ + mWindGen = NULL; + LLAudioEngine::init(num_channels, userdata); + + if(!alutInit(NULL, NULL)) + { + llwarns << "LLAudioEngine_OpenAL::init() ALUT initialization failed: " << alutGetErrorString (alutGetError ()) << llendl; + return false; + } + + llinfos << "LLAudioEngine_OpenAL::init() OpenAL successfully initialized" << llendl; + + llinfos << "OpenAL version: " + << ll_safe_string(alGetString(AL_VERSION)) << llendl; + llinfos << "OpenAL vendor: " + << ll_safe_string(alGetString(AL_VENDOR)) << llendl; + llinfos << "OpenAL renderer: " + << ll_safe_string(alGetString(AL_RENDERER)) << llendl; + + ALint major = alutGetMajorVersion (); + ALint minor = alutGetMinorVersion (); + llinfos << "ALUT version: " << major << "." << minor << llendl; + + ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext()); + + alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &major); + alcGetIntegerv(device, ALC_MAJOR_VERSION, 1, &minor); + llinfos << "ALC version: " << major << "." << minor << llendl; + + llinfos << "ALC default device: " + << ll_safe_string(alcGetString(device, + ALC_DEFAULT_DEVICE_SPECIFIER)) + << llendl; + + return true; +} + +// virtual +std::string LLAudioEngine_OpenAL::getDriverName(bool verbose) +{ + ALCdevice *device = alcGetContextsDevice(alcGetCurrentContext()); + std::ostringstream version; + + version << + "OpenAL"; + + if (verbose) + { + version << + ", version " << + ll_safe_string(alGetString(AL_VERSION)) << + " / " << + ll_safe_string(alGetString(AL_VENDOR)) << + " / " << + ll_safe_string(alGetString(AL_RENDERER)); + + if (device) + version << + ": " << + ll_safe_string(alcGetString(device, + ALC_DEFAULT_DEVICE_SPECIFIER)); + } + + return version.str(); +} + +// virtual +void LLAudioEngine_OpenAL::allocateListener() +{ + mListenerp = (LLListener *) new LLListener_OpenAL(); + if(!mListenerp) + { + llwarns << "LLAudioEngine_OpenAL::allocateListener() Listener creation failed" << llendl; + } +} + +// virtual +void LLAudioEngine_OpenAL::shutdown() +{ + llinfos << "About to LLAudioEngine::shutdown()" << llendl; + LLAudioEngine::shutdown(); + + llinfos << "About to alutExit()" << llendl; + if(!alutExit()) + { + llwarns << "Nuts." << llendl; + llwarns << "LLAudioEngine_OpenAL::shutdown() ALUT shutdown failed: " << alutGetErrorString (alutGetError ()) << llendl; + } + + llinfos << "LLAudioEngine_OpenAL::shutdown() OpenAL successfully shut down" << llendl; + + delete mListenerp; + mListenerp = NULL; +} + +LLAudioBuffer *LLAudioEngine_OpenAL::createBuffer() +{ + return new LLAudioBufferOpenAL(); +} + +LLAudioChannel *LLAudioEngine_OpenAL::createChannel() +{ + return new LLAudioChannelOpenAL(); +} + +void LLAudioEngine_OpenAL::setInternalGain(F32 gain) +{ + //llinfos << "LLAudioEngine_OpenAL::setInternalGain() Gain: " << gain << llendl; + alListenerf(AL_GAIN, gain); +} + +LLAudioChannelOpenAL::LLAudioChannelOpenAL() + : + mALSource(AL_NONE), + mLastSamplePos(0) +{ + alGenSources(1, &mALSource); +} + +LLAudioChannelOpenAL::~LLAudioChannelOpenAL() +{ + cleanup(); + alDeleteSources(1, &mALSource); +} + +void LLAudioChannelOpenAL::cleanup() +{ + alSourceStop(mALSource); + mCurrentBufferp = NULL; +} + +void LLAudioChannelOpenAL::play() +{ + if (mALSource == AL_NONE) + { + llwarns << "Playing without a mALSource, aborting" << llendl; + return; + } + + if(!isPlaying()) + { + alSourcePlay(mALSource); + getSource()->setPlayedOnce(true); + } +} + +void LLAudioChannelOpenAL::playSynced(LLAudioChannel *channelp) +{ + if (channelp) + { + LLAudioChannelOpenAL *masterchannelp = + (LLAudioChannelOpenAL*)channelp; + if (mALSource != AL_NONE && + masterchannelp->mALSource != AL_NONE) + { + // we have channels allocated to master and slave + ALfloat master_offset; + alGetSourcef(masterchannelp->mALSource, AL_SEC_OFFSET, + &master_offset); + + llinfos << "Syncing with master at " << master_offset + << "sec" << llendl; + // *TODO: detect when this fails, maybe use AL_SAMPLE_ + alSourcef(mALSource, AL_SEC_OFFSET, master_offset); + } + } + play(); +} + +bool LLAudioChannelOpenAL::isPlaying() +{ + if (mALSource != AL_NONE) + { + ALint state; + alGetSourcei(mALSource, AL_SOURCE_STATE, &state); + if(state == AL_PLAYING) + { + return true; + } + } + + return false; +} + +bool LLAudioChannelOpenAL::updateBuffer() +{ + if (LLAudioChannel::updateBuffer()) + { + // Base class update returned true, which means that we need to actually + // set up the source for a different buffer. + LLAudioBufferOpenAL *bufferp = (LLAudioBufferOpenAL *)mCurrentSourcep->getCurrentBuffer(); + ALuint buffer = bufferp->getBuffer(); + alSourcei(mALSource, AL_BUFFER, buffer); + mLastSamplePos = 0; + } + + if (mCurrentSourcep) + { + alSourcef(mALSource, AL_GAIN, + mCurrentSourcep->getGain() * getSecondaryGain()); + alSourcei(mALSource, AL_LOOPING, + mCurrentSourcep->isLoop() ? AL_TRUE : AL_FALSE); + alSourcef(mALSource, AL_ROLLOFF_FACTOR, + gAudiop->mListenerp->getRolloffFactor()); + } + + return true; +} + + +void LLAudioChannelOpenAL::updateLoop() +{ + if (mALSource == AL_NONE) + { + return; + } + + // Hack: We keep track of whether we looped or not by seeing when the + // sample position looks like it's going backwards. Not reliable; may + // yield false negatives. + // + ALint cur_pos; + alGetSourcei(mALSource, AL_SAMPLE_OFFSET, &cur_pos); + if (cur_pos < mLastSamplePos) + { + mLoopedThisFrame = true; + } + mLastSamplePos = cur_pos; +} + + +void LLAudioChannelOpenAL::update3DPosition() +{ + if(!mCurrentSourcep) + { + return; + } + if (mCurrentSourcep->isAmbient()) + { + alSource3f(mALSource, AL_POSITION, 0.0, 0.0, 0.0); + alSource3f(mALSource, AL_VELOCITY, 0.0, 0.0, 0.0); + alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_TRUE); + } else { + LLVector3 float_pos; + float_pos.setVec(mCurrentSourcep->getPositionGlobal()); + alSourcefv(mALSource, AL_POSITION, float_pos.mV); + alSourcefv(mALSource, AL_VELOCITY, mCurrentSourcep->getVelocity().mV); + alSourcei (mALSource, AL_SOURCE_RELATIVE, AL_FALSE); + } + + alSourcef(mALSource, AL_GAIN, mCurrentSourcep->getGain() * getSecondaryGain()); +} + +LLAudioBufferOpenAL::LLAudioBufferOpenAL() +{ + mALBuffer = AL_NONE; +} + +LLAudioBufferOpenAL::~LLAudioBufferOpenAL() +{ + cleanup(); +} + +void LLAudioBufferOpenAL::cleanup() +{ + if(mALBuffer != AL_NONE) + { + alDeleteBuffers(1, &mALBuffer); + mALBuffer = AL_NONE; + } +} + +bool LLAudioBufferOpenAL::loadWAV(const std::string& filename) +{ + cleanup(); + mALBuffer = alutCreateBufferFromFile(filename.c_str()); + if(mALBuffer == AL_NONE) + { + ALenum error = alutGetError(); + if (gDirUtilp->fileExists(filename)) + { + llwarns << + "LLAudioBufferOpenAL::loadWAV() Error loading " + << filename + << " " << alutGetErrorString(error) << llendl; + } + else + { + // It's common for the file to not actually exist. + lldebugs << + "LLAudioBufferOpenAL::loadWAV() Error loading " + << filename + << " " << alutGetErrorString(error) << llendl; + } + return false; + } + + return true; +} + +U32 LLAudioBufferOpenAL::getLength() +{ + if(mALBuffer == AL_NONE) + { + return 0; + } + ALint length; + alGetBufferi(mALBuffer, AL_SIZE, &length); + return length / 2; // convert size in bytes to size in (16-bit) samples +} + +// ------------ + +void LLAudioEngine_OpenAL::initWind() +{ + ALenum error; + llinfos << "LLAudioEngine_OpenAL::initWind() start" << llendl; + + mNumEmptyWindALBuffers = MAX_NUM_WIND_BUFFERS; + + alGetError(); /* clear error */ + + alGenSources(1,&mWindSource); + + if((error=alGetError()) != AL_NO_ERROR) + { + llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind sources: "<<error<<llendl; + } + + mWindGen = new LLWindGen<WIND_SAMPLE_T>; + + mWindBufFreq = mWindGen->getInputSamplingRate(); + mWindBufSamples = llceil(mWindBufFreq * WIND_BUFFER_SIZE_SEC); + mWindBufBytes = mWindBufSamples * 2 /*stereo*/ * sizeof(WIND_SAMPLE_T); + + mWindBuf = new WIND_SAMPLE_T [mWindBufSamples * 2 /*stereo*/]; + + if(mWindBuf==NULL) + { + llerrs << "LLAudioEngine_OpenAL::initWind() Error creating wind memory buffer" << llendl; + mEnableWind=false; + } + + llinfos << "LLAudioEngine_OpenAL::initWind() done" << llendl; +} + +void LLAudioEngine_OpenAL::cleanupWind() +{ + llinfos << "LLAudioEngine_OpenAL::cleanupWind()" << llendl; + + if (mWindSource != AL_NONE) + { + // detach and delete all outstanding buffers on the wind source + alSourceStop(mWindSource); + ALint processed; + alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed); + while (processed--) + { + ALuint buffer = AL_NONE; + alSourceUnqueueBuffers(mWindSource, 1, &buffer); + alDeleteBuffers(1, &buffer); + } + + // delete the wind source itself + alDeleteSources(1, &mWindSource); + + mWindSource = AL_NONE; + } + + delete[] mWindBuf; + mWindBuf = NULL; + + delete mWindGen; + mWindGen = NULL; +} + +void LLAudioEngine_OpenAL::updateWind(LLVector3 wind_vec, F32 camera_altitude) +{ + LLVector3 wind_pos; + F64 pitch; + F64 center_freq; + ALenum error; + + if (!mEnableWind) + return; + + if(!mWindBuf) + return; + + if (mWindUpdateTimer.checkExpirationAndReset(LL_WIND_UPDATE_INTERVAL)) + { + + // wind comes in as Linden coordinate (+X = forward, +Y = left, +Z = up) + // need to convert this to the conventional orientation DS3D and OpenAL use + // where +X = right, +Y = up, +Z = backwards + + wind_vec.setVec(-wind_vec.mV[1], wind_vec.mV[2], -wind_vec.mV[0]); + + pitch = 1.0 + mapWindVecToPitch(wind_vec); + center_freq = 80.0 * pow(pitch,2.5*(mapWindVecToGain(wind_vec)+1.0)); + + mWindGen->mTargetFreq = (F32)center_freq; + mWindGen->mTargetGain = (F32)mapWindVecToGain(wind_vec) * mMaxWindGain; + mWindGen->mTargetPanGainR = (F32)mapWindVecToPan(wind_vec); + + alSourcei(mWindSource, AL_LOOPING, AL_FALSE); + alSource3f(mWindSource, AL_POSITION, 0.0, 0.0, 0.0); + alSource3f(mWindSource, AL_VELOCITY, 0.0, 0.0, 0.0); + alSourcef(mWindSource, AL_ROLLOFF_FACTOR, 0.0); + alSourcei(mWindSource, AL_SOURCE_RELATIVE, AL_TRUE); + } + + // ok lets make a wind buffer now + + ALint processed, queued, unprocessed; + alGetSourcei(mWindSource, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(mWindSource, AL_BUFFERS_QUEUED, &queued); + unprocessed = queued - processed; + + // ensure that there are always at least 3x as many filled buffers + // queued as we managed to empty since last time. + mNumEmptyWindALBuffers = llmin(mNumEmptyWindALBuffers + processed * 3 - unprocessed, MAX_NUM_WIND_BUFFERS-unprocessed); + mNumEmptyWindALBuffers = llmax(mNumEmptyWindALBuffers, 0); + + //llinfos << "mNumEmptyWindALBuffers: " << mNumEmptyWindALBuffers <<" (" << unprocessed << ":" << processed << ")" << llendl; + + while(processed--) // unqueue old buffers + { + ALuint buffer; + ALenum error; + alGetError(); /* clear error */ + alSourceUnqueueBuffers(mWindSource, 1, &buffer); + error = alGetError(); + if(error != AL_NO_ERROR) + { + llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (unqueuing) buffers" << llendl; + } + else + { + alDeleteBuffers(1, &buffer); + } + } + + unprocessed += mNumEmptyWindALBuffers; + while (mNumEmptyWindALBuffers > 0) // fill+queue new buffers + { + ALuint buffer; + alGetError(); /* clear error */ + alGenBuffers(1,&buffer); + if((error=alGetError()) != AL_NO_ERROR) + { + llwarns << "LLAudioEngine_OpenAL::initWind() Error creating wind buffer: " << error << llendl; + break; + } + + alBufferData(buffer, + AL_FORMAT_STEREO16, + mWindGen->windGenerate(mWindBuf, + mWindBufSamples, 2), + mWindBufBytes, + mWindBufFreq); + error = alGetError(); + if(error != AL_NO_ERROR) + { + llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (bufferdata) buffers" << llendl; + } + + alSourceQueueBuffers(mWindSource, 1, &buffer); + error = alGetError(); + if(error != AL_NO_ERROR) + { + llwarns << "LLAudioEngine_OpenAL::updateWind() error swapping (queuing) buffers" << llendl; + } + + --mNumEmptyWindALBuffers; + } + + ALint playing; + alGetSourcei(mWindSource, AL_SOURCE_STATE, &playing); + if(playing != AL_PLAYING) + { + alSourcePlay(mWindSource); + + lldebugs << "Wind had stopped - probably ran out of buffers - restarting: " << (unprocessed+mNumEmptyWindALBuffers) << " now queued." << llendl; + } +} + diff --git a/indra/llaudio/llaudioengine_openal.h b/indra/llaudio/llaudioengine_openal.h new file mode 100644 index 0000000000..5aca03e195 --- /dev/null +++ b/indra/llaudio/llaudioengine_openal.h @@ -0,0 +1,114 @@ +/** + * @file audioengine_openal.cpp + * @brief implementation of audio engine using OpenAL + * support as a OpenAL 3D implementation + * + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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_AUDIOENGINE_OPENAL_H +#define LL_AUDIOENGINE_OPENAL_H + +#include "llaudioengine.h" +#include "lllistener_openal.h" +#include "llwindgen.h" + +class LLAudioEngine_OpenAL : public LLAudioEngine +{ + public: + LLAudioEngine_OpenAL(); + virtual ~LLAudioEngine_OpenAL(); + + virtual bool init(const S32 num_channels, void *user_data); + virtual std::string getDriverName(bool verbose); + virtual void allocateListener(); + + virtual void shutdown(); + + void setInternalGain(F32 gain); + + LLAudioBuffer* createBuffer(); + LLAudioChannel* createChannel(); + + /*virtual*/ void initWind(); + /*virtual*/ void cleanupWind(); + /*virtual*/ void updateWind(LLVector3 direction, F32 camera_altitude); + + private: + void * windDSP(void *newbuffer, int length); + typedef S16 WIND_SAMPLE_T; + LLWindGen<WIND_SAMPLE_T> *mWindGen; + S16 *mWindBuf; + U32 mWindBufFreq; + U32 mWindBufSamples; + U32 mWindBufBytes; + ALuint mWindSource; + int mNumEmptyWindALBuffers; + + static const int MAX_NUM_WIND_BUFFERS = 80; + static const float WIND_BUFFER_SIZE_SEC = 0.05f; // 1/20th sec +}; + +class LLAudioChannelOpenAL : public LLAudioChannel +{ + public: + LLAudioChannelOpenAL(); + virtual ~LLAudioChannelOpenAL(); + protected: + /*virtual*/ void play(); + /*virtual*/ void playSynced(LLAudioChannel *channelp); + /*virtual*/ void cleanup(); + /*virtual*/ bool isPlaying(); + + /*virtual*/ bool updateBuffer(); + /*virtual*/ void update3DPosition(); + /*virtual*/ void updateLoop(); + + ALuint mALSource; + ALint mLastSamplePos; +}; + +class LLAudioBufferOpenAL : public LLAudioBuffer{ + public: + LLAudioBufferOpenAL(); + virtual ~LLAudioBufferOpenAL(); + + bool loadWAV(const std::string& filename); + U32 getLength(); + + friend class LLAudioChannelOpenAL; + protected: + void cleanup(); + ALuint getBuffer() {return mALBuffer;} + + ALuint mALBuffer; +}; + +#endif diff --git a/indra/llaudio/lllistener.cpp b/indra/llaudio/lllistener.cpp new file mode 100644 index 0000000000..846c6bccb5 --- /dev/null +++ b/indra/llaudio/lllistener.cpp @@ -0,0 +1,142 @@ +/** + * @file listener.cpp + * @brief Implementation of LISTENER class abstracting the audio support + * + * $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 "lllistener.h" + +#define DEFAULT_AT 0.0f,0.0f,-1.0f +#define DEFAULT_UP 0.0f,1.0f,0.0f + +//----------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------- +LLListener::LLListener() +{ + init(); +} + +//----------------------------------------------------------------------- +LLListener::~LLListener() +{ +} + +//----------------------------------------------------------------------- +void LLListener::init(void) +{ + mPosition.zeroVec(); + mListenAt.setVec(DEFAULT_AT); + mListenUp.setVec(DEFAULT_UP); + mVelocity.zeroVec(); +} + +//----------------------------------------------------------------------- +void LLListener::translate(LLVector3 offset) +{ + mPosition += offset; +} + +//----------------------------------------------------------------------- +void LLListener::setPosition(LLVector3 pos) +{ + mPosition = pos; +} + +//----------------------------------------------------------------------- +LLVector3 LLListener::getPosition(void) +{ + return(mPosition); +} + +//----------------------------------------------------------------------- +LLVector3 LLListener::getAt(void) +{ + return(mListenAt); +} + +//----------------------------------------------------------------------- +LLVector3 LLListener::getUp(void) +{ + return(mListenUp); +} + +//----------------------------------------------------------------------- +void LLListener::setVelocity(LLVector3 vel) +{ + mVelocity = vel; +} + +//----------------------------------------------------------------------- +void LLListener::orient(LLVector3 up, LLVector3 at) +{ + mListenUp = up; + mListenAt = at; +} + +//----------------------------------------------------------------------- +void LLListener::set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at) +{ + mPosition = pos; + mVelocity = vel; + + setPosition(pos); + setVelocity(vel); + orient(up,at); +} + +//----------------------------------------------------------------------- +void LLListener::setDopplerFactor(F32 factor) +{ +} + +//----------------------------------------------------------------------- +F32 LLListener::getDopplerFactor() +{ + return (1.f); +} + +//----------------------------------------------------------------------- +void LLListener::setRolloffFactor(F32 factor) +{ +} + +//----------------------------------------------------------------------- +F32 LLListener::getRolloffFactor() +{ + return (1.f); +} + +//----------------------------------------------------------------------- +void LLListener::commitDeferredChanges() +{ +} + diff --git a/indra/llaudio/lllistener.h b/indra/llaudio/lllistener.h new file mode 100644 index 0000000000..e94fbe853f --- /dev/null +++ b/indra/llaudio/lllistener.h @@ -0,0 +1,78 @@ +/** + * @file listener.h + * @brief Description of LISTENER base class abstracting the audio support. + * + * $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_LISTENER_H +#define LL_LISTENER_H + +#include "v3math.h" + +class LLListener +{ + private: + protected: + LLVector3 mPosition; + LLVector3 mVelocity; + LLVector3 mListenAt; + LLVector3 mListenUp; + + public: + + private: + protected: + public: + LLListener(); + virtual ~LLListener(); + virtual void init(); + + virtual void set(LLVector3 pos, LLVector3 vel, LLVector3 up, LLVector3 at); + + virtual void setPosition(LLVector3 pos); + virtual void setVelocity(LLVector3 vel); + + virtual void orient(LLVector3 up, LLVector3 at); + virtual void translate(LLVector3 offset); + + virtual void setDopplerFactor(F32 factor); + virtual void setRolloffFactor(F32 factor); + + virtual LLVector3 getPosition(); + virtual LLVector3 getAt(); + virtual LLVector3 getUp(); + + virtual F32 getDopplerFactor(); + virtual F32 getRolloffFactor(); + + virtual void commitDeferredChanges(); +}; + +#endif + diff --git a/indra/llaudio/lllistener_ds3d.h b/indra/llaudio/lllistener_ds3d.h new file mode 100644 index 0000000000..1ff9c170c4 --- /dev/null +++ b/indra/llaudio/lllistener_ds3d.h @@ -0,0 +1,74 @@ +/** + * @file listener_ds3d.h + * @brief Description of LISTENER class abstracting the audio support + * as a DirectSound 3D implementation (windows only) + * + * $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_LISTENER_DS3D_H +#define LL_LISTENER_DS3D_H + +#include "lllistener.h" + +#include <dmusici.h> +#include <dsound.h> +#include <ks.h> + +class LLListener_DS3D : public LLListener +{ + private: + protected: + IDirectSound3DListener8 *m3DListener; + public: + + private: + protected: + public: + LLListener_DS3D(); + virtual ~LLListener_DS3D(); + virtual void init(); + + virtual void setDS3DLPtr (IDirectSound3DListener8 *listener_p); + + virtual void translate(LLVector3 offset); + virtual void setPosition(LLVector3 pos); + virtual void setVelocity(LLVector3 vel); + virtual void orient(LLVector3 up, LLVector3 at); + + virtual void setDopplerFactor(F32 factor); + virtual F32 getDopplerFactor(); + virtual void setRolloffFactor(F32 factor); + virtual F32 getRolloffFactor(); + + virtual void commitDeferredChanges(); +}; + +#endif + + diff --git a/indra/llaudio/lllistener_fmod.cpp b/indra/llaudio/lllistener_fmod.cpp new file mode 100644 index 0000000000..57ad461b02 --- /dev/null +++ b/indra/llaudio/lllistener_fmod.cpp @@ -0,0 +1,131 @@ +/** + * @file listener_fmod.cpp + * @brief implementation of LISTENER class abstracting the audio + * support as a FMOD 3D implementation (windows only) + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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 "llaudioengine.h" +#include "lllistener_fmod.h" +#include "fmod.h" + +//----------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------- +LLListener_FMOD::LLListener_FMOD() +{ + init(); +} + +//----------------------------------------------------------------------- +LLListener_FMOD::~LLListener_FMOD() +{ +} + +//----------------------------------------------------------------------- +void LLListener_FMOD::init(void) +{ + // do inherited + LLListener::init(); + mDopplerFactor = 1.0f; + mRolloffFactor = 1.0f; +} + +//----------------------------------------------------------------------- +void LLListener_FMOD::translate(LLVector3 offset) +{ + LLListener::translate(offset); + + FSOUND_3D_Listener_SetAttributes(mPosition.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]); +} + +//----------------------------------------------------------------------- +void LLListener_FMOD::setPosition(LLVector3 pos) +{ + LLListener::setPosition(pos); + + FSOUND_3D_Listener_SetAttributes(pos.mV, NULL, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]); +} + +//----------------------------------------------------------------------- +void LLListener_FMOD::setVelocity(LLVector3 vel) +{ + LLListener::setVelocity(vel); + + FSOUND_3D_Listener_SetAttributes(NULL, vel.mV, mListenAt.mV[0],mListenAt.mV[1],mListenAt.mV[2], mListenUp.mV[0],mListenUp.mV[1],mListenUp.mV[2]); +} + +//----------------------------------------------------------------------- +void LLListener_FMOD::orient(LLVector3 up, LLVector3 at) +{ + LLListener::orient(up, at); + + // Welcome to the transition between right and left + // (coordinate systems, that is) + // Leaving the at vector alone results in a L/R reversal + // since DX is left-handed and we (LL, OpenGL, OpenAL) are right-handed + at = -at; + + FSOUND_3D_Listener_SetAttributes(NULL, NULL, at.mV[0],at.mV[1],at.mV[2], up.mV[0],up.mV[1],up.mV[2]); +} + +//----------------------------------------------------------------------- +void LLListener_FMOD::commitDeferredChanges() +{ + FSOUND_Update(); +} + + +void LLListener_FMOD::setRolloffFactor(F32 factor) +{ + mRolloffFactor = factor; + FSOUND_3D_SetRolloffFactor(factor); +} + + +F32 LLListener_FMOD::getRolloffFactor() +{ + return mRolloffFactor; +} + + +void LLListener_FMOD::setDopplerFactor(F32 factor) +{ + mDopplerFactor = factor; + FSOUND_3D_SetDopplerFactor(factor); +} + + +F32 LLListener_FMOD::getDopplerFactor() +{ + return mDopplerFactor; +} + + diff --git a/indra/llaudio/lllistener_fmod.h b/indra/llaudio/lllistener_fmod.h new file mode 100644 index 0000000000..5a48ec8b68 --- /dev/null +++ b/indra/llaudio/lllistener_fmod.h @@ -0,0 +1,64 @@ +/** + * @file listener_fmod.h + * @brief Description of LISTENER class abstracting the audio support + * as an FMOD 3D implementation (windows and Linux) + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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_LISTENER_FMOD_H +#define LL_LISTENER_FMOD_H + +#include "lllistener.h" + +class LLListener_FMOD : public LLListener +{ + public: + LLListener_FMOD(); + virtual ~LLListener_FMOD(); + virtual void init(); + + virtual void translate(LLVector3 offset); + virtual void setPosition(LLVector3 pos); + virtual void setVelocity(LLVector3 vel); + virtual void orient(LLVector3 up, LLVector3 at); + virtual void commitDeferredChanges(); + + virtual void setDopplerFactor(F32 factor); + virtual F32 getDopplerFactor(); + virtual void setRolloffFactor(F32 factor); + virtual F32 getRolloffFactor(); + + protected: + F32 mDopplerFactor; + F32 mRolloffFactor; +}; + +#endif + + diff --git a/indra/llaudio/lllistener_openal.cpp b/indra/llaudio/lllistener_openal.cpp new file mode 100644 index 0000000000..a96ebd5dba --- /dev/null +++ b/indra/llaudio/lllistener_openal.cpp @@ -0,0 +1,116 @@ +/** + * @file audioengine_openal.cpp + * @brief implementation of audio engine using OpenAL + * support as a OpenAL 3D implementation + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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 "llaudioengine.h" + +#include "lllistener_openal.h" + +LLListener_OpenAL::LLListener_OpenAL() +{ + init(); +} + +LLListener_OpenAL::~LLListener_OpenAL() +{ +} + +void LLListener_OpenAL::translate(LLVector3 offset) +{ + //llinfos << "LLListener_OpenAL::translate() : " << offset << llendl; + LLListener::translate(offset); +} + +void LLListener_OpenAL::setPosition(LLVector3 pos) +{ + //llinfos << "LLListener_OpenAL::setPosition() : " << pos << llendl; + LLListener::setPosition(pos); +} + +void LLListener_OpenAL::setVelocity(LLVector3 vel) +{ + LLListener::setVelocity(vel); +} + +void LLListener_OpenAL::orient(LLVector3 up, LLVector3 at) +{ + //llinfos << "LLListener_OpenAL::orient() up: " << up << " at: " << at << llendl; + LLListener::orient(up, at); +} + +void LLListener_OpenAL::commitDeferredChanges() +{ + ALfloat orientation[6]; + orientation[0] = mListenAt.mV[0]; + orientation[1] = mListenAt.mV[1]; + orientation[2] = mListenAt.mV[2]; + orientation[3] = mListenUp.mV[0]; + orientation[4] = mListenUp.mV[1]; + orientation[5] = mListenUp.mV[2]; + + ALfloat velocity[3]; + velocity[0] = mVelocity.mV[0]; + velocity[1] = mVelocity.mV[1]; + velocity[2] = mVelocity.mV[2]; + + alListenerfv(AL_ORIENTATION, orientation); + alListenerfv(AL_POSITION, mPosition.mV); + alListenerfv(AL_VELOCITY, velocity); +} + +void LLListener_OpenAL::setDopplerFactor(F32 factor) +{ + //llinfos << "LLListener_OpenAL::setDopplerFactor() : " << factor << llendl; + alDopplerFactor(factor); +} + +F32 LLListener_OpenAL::getDopplerFactor() +{ + ALfloat factor; + factor = alGetFloat(AL_DOPPLER_FACTOR); + //llinfos << "LLListener_OpenAL::getDopplerFactor() : " << factor << llendl; + return factor; +} + + +void LLListener_OpenAL::setRolloffFactor(F32 factor) +{ + mRolloffFactor = factor; +} + +F32 LLListener_OpenAL::getRolloffFactor() +{ + return mRolloffFactor; +} + + diff --git a/indra/llaudio/lllistener_openal.h b/indra/llaudio/lllistener_openal.h new file mode 100644 index 0000000000..0dfeea5c90 --- /dev/null +++ b/indra/llaudio/lllistener_openal.h @@ -0,0 +1,64 @@ +/** + * @file listener_openal.h + * @brief Description of LISTENER class abstracting the audio support + * as an OpenAL 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$ + */ + +#ifndef LL_LISTENER_OPENAL_H +#define LL_LISTENER_OPENAL_H + +#include "lllistener.h" + +#include "AL/al.h" +#include "AL/alut.h" + +class LLListener_OpenAL : public LLListener +{ + public: + LLListener_OpenAL(); + virtual ~LLListener_OpenAL(); + + virtual void translate(LLVector3 offset); + virtual void setPosition(LLVector3 pos); + virtual void setVelocity(LLVector3 vel); + virtual void orient(LLVector3 up, LLVector3 at); + virtual void commitDeferredChanges(); + + virtual void setDopplerFactor(F32 factor); + virtual F32 getDopplerFactor(); + virtual void setRolloffFactor(F32 factor); + virtual F32 getRolloffFactor(); + + protected: + F32 mRolloffFactor; +}; + +#endif + diff --git a/indra/llaudio/llstreamingaudio.h b/indra/llaudio/llstreamingaudio.h new file mode 100644 index 0000000000..aa89e6a177 --- /dev/null +++ b/indra/llaudio/llstreamingaudio.h @@ -0,0 +1,56 @@ +/** + * @file streamingaudio.h + * @author Tofu Linden + * @brief Definition of LLStreamingAudioInterface base class abstracting the streaming audio interface + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_STREAMINGAUDIO_H +#define LL_STREAMINGAUDIO_H + +#include "stdtypes.h" // from llcommon + +// Entirely abstract. Based exactly on the historic API. +class LLStreamingAudioInterface +{ + public: + virtual ~LLStreamingAudioInterface() {} + + virtual void start(const std::string& url) = 0; + virtual void stop() = 0; + virtual void pause(int pause) = 0; + virtual void update() = 0; + virtual int isPlaying() = 0; + // use a value from 0.0 to 1.0, inclusive + virtual void setGain(F32 vol) = 0; + virtual F32 getGain() = 0; + virtual std::string getURL() = 0; +}; + +#endif // LL_STREAMINGAUDIO_H diff --git a/indra/llaudio/llstreamingaudio_fmod.cpp b/indra/llaudio/llstreamingaudio_fmod.cpp new file mode 100644 index 0000000000..a71a87203c --- /dev/null +++ b/indra/llaudio/llstreamingaudio_fmod.cpp @@ -0,0 +1,362 @@ +/** + * @file streamingaudio_fmod.cpp + * @brief LLStreamingAudio_FMOD implementation + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 "llmath.h" + +#include "fmod.h" +#include "fmod_errors.h" + +#include "llstreamingaudio_fmod.h" + + +class LLAudioStreamManagerFMOD +{ +public: + LLAudioStreamManagerFMOD(const std::string& url); + int startStream(); + bool stopStream(); // Returns true if the stream was successfully stopped. + bool ready(); + + const std::string& getURL() { return mInternetStreamURL; } + + int getOpenState(); +protected: + FSOUND_STREAM* mInternetStream; + bool mReady; + + std::string mInternetStreamURL; +}; + + + +//--------------------------------------------------------------------------- +// Internet Streaming +//--------------------------------------------------------------------------- +LLStreamingAudio_FMOD::LLStreamingAudio_FMOD() : + mCurrentInternetStreamp(NULL), + mFMODInternetStreamChannel(-1), + mGain(1.0f) +{ + // Number of milliseconds of audio to buffer for the audio card. + // Must be larger than the usual Second Life frame stutter time. + FSOUND_Stream_SetBufferSize(200); + + // Here's where we set the size of the network buffer and some buffering + // parameters. In this case we want a network buffer of 16k, we want it + // to prebuffer 40% of that when we first connect, and we want it + // to rebuffer 80% of that whenever we encounter a buffer underrun. + + // Leave the net buffer properties at the default. + //FSOUND_Stream_Net_SetBufferProperties(20000, 40, 80); +} + + +LLStreamingAudio_FMOD::~LLStreamingAudio_FMOD() +{ + // nothing interesting/safe to do. +} + + +void LLStreamingAudio_FMOD::start(const std::string& url) +{ + //if (!mInited) + //{ + // llwarns << "startInternetStream before audio initialized" << llendl; + // return; + //} + + // "stop" stream but don't clear url, etc. in case url == mInternetStreamURL + stop(); + + if (!url.empty()) + { + llinfos << "Starting internet stream: " << url << llendl; + mCurrentInternetStreamp = new LLAudioStreamManagerFMOD(url); + mURL = url; + } + else + { + llinfos << "Set internet stream to null" << llendl; + mURL.clear(); + } +} + + +void LLStreamingAudio_FMOD::update() +{ + // Kill dead internet streams, if possible + std::list<LLAudioStreamManagerFMOD *>::iterator iter; + for (iter = mDeadStreams.begin(); iter != mDeadStreams.end();) + { + LLAudioStreamManagerFMOD *streamp = *iter; + if (streamp->stopStream()) + { + llinfos << "Closed dead stream" << llendl; + delete streamp; + mDeadStreams.erase(iter++); + } + else + { + iter++; + } + } + + // Don't do anything if there are no streams playing + if (!mCurrentInternetStreamp) + { + return; + } + + int open_state = mCurrentInternetStreamp->getOpenState(); + + if (!open_state) + { + // Stream is live + + // start the stream if it's ready + if (mFMODInternetStreamChannel < 0) + { + mFMODInternetStreamChannel = mCurrentInternetStreamp->startStream(); + + if (mFMODInternetStreamChannel != -1) + { + // Reset volume to previously set volume + setGain(getGain()); + FSOUND_SetPaused(mFMODInternetStreamChannel, false); + } + } + } + + switch(open_state) + { + default: + case 0: + // success + break; + case -1: + // stream handle is invalid + llwarns << "InternetStream - invalid handle" << llendl; + stop(); + return; + case -2: + // opening + break; + case -3: + // failed to open, file not found, perhaps + llwarns << "InternetSteam - failed to open" << llendl; + stop(); + return; + case -4: + // connecting + break; + case -5: + // buffering + break; + } + +} + +void LLStreamingAudio_FMOD::stop() +{ + if (mFMODInternetStreamChannel != -1) + { + FSOUND_SetPaused(mFMODInternetStreamChannel, true); + FSOUND_SetPriority(mFMODInternetStreamChannel, 0); + mFMODInternetStreamChannel = -1; + } + + if (mCurrentInternetStreamp) + { + llinfos << "Stopping internet stream: " << mCurrentInternetStreamp->getURL() << llendl; + if (mCurrentInternetStreamp->stopStream()) + { + delete mCurrentInternetStreamp; + } + else + { + llwarns << "Pushing stream to dead list: " << mCurrentInternetStreamp->getURL() << llendl; + mDeadStreams.push_back(mCurrentInternetStreamp); + } + mCurrentInternetStreamp = NULL; + //mURL.clear(); + } +} + +void LLStreamingAudio_FMOD::pause(int pauseopt) +{ + if (pauseopt < 0) + { + pauseopt = mCurrentInternetStreamp ? 1 : 0; + } + + if (pauseopt) + { + if (mCurrentInternetStreamp) + { + stop(); + } + } + else + { + start(getURL()); + } +} + + +// A stream is "playing" if it has been requested to start. That +// doesn't necessarily mean audio is coming out of the speakers. +int LLStreamingAudio_FMOD::isPlaying() +{ + if (mCurrentInternetStreamp) + { + return 1; // Active and playing + } + else if (!mURL.empty()) + { + return 2; // "Paused" + } + else + { + return 0; + } +} + + +F32 LLStreamingAudio_FMOD::getGain() +{ + return mGain; +} + + +std::string LLStreamingAudio_FMOD::getURL() +{ + return mURL; +} + + +void LLStreamingAudio_FMOD::setGain(F32 vol) +{ + mGain = vol; + + if (mFMODInternetStreamChannel != -1) + { + vol = llclamp(vol, 0.f, 1.f); + int vol_int = llround(vol * 255.f); + FSOUND_SetVolumeAbsolute(mFMODInternetStreamChannel, vol_int); + } +} + + +/////////////////////////////////////////////////////// +// manager of possibly-multiple internet audio streams + +LLAudioStreamManagerFMOD::LLAudioStreamManagerFMOD(const std::string& url) : + mInternetStream(NULL), + mReady(false) +{ + mInternetStreamURL = url; + mInternetStream = FSOUND_Stream_Open(url.c_str(), FSOUND_NORMAL | FSOUND_NONBLOCKING, 0, 0); + if (!mInternetStream) + { + llwarns << "Couldn't open fmod stream, error " + << FMOD_ErrorString(FSOUND_GetError()) + << llendl; + mReady = false; + return; + } + + mReady = true; +} + +int LLAudioStreamManagerFMOD::startStream() +{ + // We need a live and opened stream before we try and play it. + if (!mInternetStream || getOpenState()) + { + llwarns << "No internet stream to start playing!" << llendl; + return -1; + } + + // Make sure the stream is set to 2D mode. + FSOUND_Stream_SetMode(mInternetStream, FSOUND_2D); + + return FSOUND_Stream_PlayEx(FSOUND_FREE, mInternetStream, NULL, true); +} + +bool LLAudioStreamManagerFMOD::stopStream() +{ + if (mInternetStream) + { + int read_percent = 0; + int status = 0; + int bitrate = 0; + unsigned int flags = 0x0; + FSOUND_Stream_Net_GetStatus(mInternetStream, &status, &read_percent, &bitrate, &flags); + + bool close = true; + switch (status) + { + case FSOUND_STREAM_NET_CONNECTING: + close = false; + break; + case FSOUND_STREAM_NET_NOTCONNECTED: + case FSOUND_STREAM_NET_BUFFERING: + case FSOUND_STREAM_NET_READY: + case FSOUND_STREAM_NET_ERROR: + default: + close = true; + } + + if (close) + { + FSOUND_Stream_Close(mInternetStream); + mInternetStream = NULL; + return true; + } + else + { + return false; + } + } + else + { + return true; + } +} + +int LLAudioStreamManagerFMOD::getOpenState() +{ + int open_state = FSOUND_Stream_GetOpenState(mInternetStream); + return open_state; +} diff --git a/indra/llaudio/llstreamingaudio_fmod.h b/indra/llaudio/llstreamingaudio_fmod.h new file mode 100644 index 0000000000..968ab53a0b --- /dev/null +++ b/indra/llaudio/llstreamingaudio_fmod.h @@ -0,0 +1,68 @@ +/** + * @file streamingaudio_fmod.h + * @author Tofu Linden + * @brief Definition of LLStreamingAudio_FMOD implementation + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_STREAMINGAUDIO_FMOD_H +#define LL_STREAMINGAUDIO_FMOD_H + +#include "stdtypes.h" // from llcommon + +#include "llstreamingaudio.h" + +class LLAudioStreamManagerFMOD; + +class LLStreamingAudio_FMOD : public LLStreamingAudioInterface +{ + public: + LLStreamingAudio_FMOD(); + /*virtual*/ ~LLStreamingAudio_FMOD(); + + /*virtual*/ void start(const std::string& url); + /*virtual*/ void stop(); + /*virtual*/ void pause(int pause); + /*virtual*/ void update(); + /*virtual*/ int isPlaying(); + /*virtual*/ void setGain(F32 vol); + /*virtual*/ F32 getGain(); + /*virtual*/ std::string getURL(); + +private: + LLAudioStreamManagerFMOD *mCurrentInternetStreamp; + int mFMODInternetStreamChannel; + std::list<LLAudioStreamManagerFMOD *> mDeadStreams; + + std::string mURL; + F32 mGain; +}; + + +#endif // LL_STREAMINGAUDIO_FMOD_H diff --git a/indra/llaudio/llvorbisencode.cpp b/indra/llaudio/llvorbisencode.cpp new file mode 100644 index 0000000000..8ee082a245 --- /dev/null +++ b/indra/llaudio/llvorbisencode.cpp @@ -0,0 +1,505 @@ +/** + * @file vorbisencode.cpp + * @brief Vorbis encoding routine routine for Indra. + * + * $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 "vorbis/vorbisenc.h" + +#include "llvorbisencode.h" +#include "llerror.h" +#include "llrand.h" +#include "llmath.h" +#include "llapr.h" + +//#if LL_DARWIN +// MBW -- XXX -- Getting rid of SecondLifeVorbis for now -- no fmod means no name collisions. +#if 0 +#include "VorbisFramework.h" + +#define vorbis_analysis mac_vorbis_analysis +#define vorbis_analysis_headerout mac_vorbis_analysis_headerout +#define vorbis_analysis_init mac_vorbis_analysis_init +#define vorbis_encode_ctl mac_vorbis_encode_ctl +#define vorbis_encode_setup_init mac_vorbis_encode_setup_init +#define vorbis_encode_setup_managed mac_vorbis_encode_setup_managed + +#define vorbis_info_init mac_vorbis_info_init +#define vorbis_info_clear mac_vorbis_info_clear +#define vorbis_comment_init mac_vorbis_comment_init +#define vorbis_comment_clear mac_vorbis_comment_clear +#define vorbis_block_init mac_vorbis_block_init +#define vorbis_block_clear mac_vorbis_block_clear +#define vorbis_dsp_clear mac_vorbis_dsp_clear +#define vorbis_analysis_buffer mac_vorbis_analysis_buffer +#define vorbis_analysis_wrote mac_vorbis_analysis_wrote +#define vorbis_analysis_blockout mac_vorbis_analysis_blockout + +#define ogg_stream_packetin mac_ogg_stream_packetin +#define ogg_stream_init mac_ogg_stream_init +#define ogg_stream_flush mac_ogg_stream_flush +#define ogg_stream_pageout mac_ogg_stream_pageout +#define ogg_page_eos mac_ogg_page_eos +#define ogg_stream_clear mac_ogg_stream_clear + +#endif + +S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg) +{ + U16 num_channels = 0; + U32 sample_rate = 0; + U32 bits_per_sample = 0; + U32 physical_file_size = 0; + U32 chunk_length = 0; + U32 raw_data_length = 0; + U32 bytes_per_sec = 0; + BOOL uncompressed_pcm = FALSE; + + unsigned char wav_header[44]; /*Flawfinder: ignore*/ + + error_msg.clear(); + + //******************************** + LLAPRFile infile ; + infile.open(in_fname,LL_APR_RB); + //******************************** + if (!infile.getFileHandle()) + { + error_msg = "CannotUploadSoundFile"; + return(LLVORBISENC_SOURCE_OPEN_ERR); + } + + infile.read(wav_header, 44); + physical_file_size = infile.seek(APR_END,0); + + if (strncmp((char *)&(wav_header[0]),"RIFF",4)) + { + error_msg = "SoundFileNotRIFF"; + return(LLVORBISENC_WAV_FORMAT_ERR); + } + + if (strncmp((char *)&(wav_header[8]),"WAVE",4)) + { + error_msg = "SoundFileNotRIFF"; + return(LLVORBISENC_WAV_FORMAT_ERR); + } + + // parse the chunks + + U32 file_pos = 12; // start at the first chunk (usually fmt but not always) + + while ((file_pos + 8)< physical_file_size) + { + infile.seek(APR_SET,file_pos); + infile.read(wav_header, 44); + + chunk_length = ((U32) wav_header[7] << 24) + + ((U32) wav_header[6] << 16) + + ((U32) wav_header[5] << 8) + + wav_header[4]; + +// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl; + + if (!(strncmp((char *)&(wav_header[0]),"fmt ",4))) + { + if ((wav_header[8] == 0x01) && (wav_header[9] == 0x00)) + { + uncompressed_pcm = TRUE; + } + num_channels = ((U16) wav_header[11] << 8) + wav_header[10]; + sample_rate = ((U32) wav_header[15] << 24) + + ((U32) wav_header[14] << 16) + + ((U32) wav_header[13] << 8) + + wav_header[12]; + bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22]; + bytes_per_sec = ((U32) wav_header[19] << 24) + + ((U32) wav_header[18] << 16) + + ((U32) wav_header[17] << 8) + + wav_header[16]; + } + else if (!(strncmp((char *)&(wav_header[0]),"data",4))) + { + raw_data_length = chunk_length; + } + file_pos += (chunk_length + 8); + chunk_length = 0; + } + //**************** + infile.close(); + //**************** + + if (!uncompressed_pcm) + { + error_msg = "SoundFileNotPCM"; + return(LLVORBISENC_PCM_FORMAT_ERR); + } + + if ((num_channels < 1) || (num_channels > 2)) + { + error_msg = "SoundFileInvalidChannelCount"; + return(LLVORBISENC_MULTICHANNEL_ERR); + } + + if (sample_rate != 44100) + { + error_msg = "SoundFileInvalidSampleRate"; + return(LLVORBISENC_UNSUPPORTED_SAMPLE_RATE); + } + + if ((bits_per_sample != 16) && (bits_per_sample != 8)) + { + error_msg = "SoundFileInvalidWordSize"; + return(LLVORBISENC_UNSUPPORTED_WORD_SIZE); + } + + if (!raw_data_length) + { + error_msg = "SoundFileInvalidHeader"; + return(LLVORBISENC_CLIP_TOO_LONG); + } + + F32 clip_length = (F32)raw_data_length/(F32)bytes_per_sec; + + if (clip_length > 10.0f) + { + error_msg = "SoundFileInvalidTooLong"; + return(LLVORBISENC_CLIP_TOO_LONG); + } + + return(LLVORBISENC_NOERR); +} + +S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname) +{ +#define READ_BUFFER 1024 + unsigned char readbuffer[READ_BUFFER*4+44]; /* out of the data segment, not the stack */ /*Flawfinder: ignore*/ + + ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + + vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment vc; /* struct that stores all the user comments */ + + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + int eos=0; + int result; + + U16 num_channels = 0; + U32 sample_rate = 0; + U32 bits_per_sample = 0; + + S32 format_error = 0; + std::string error_msg; + if ((format_error = check_for_invalid_wav_formats(in_fname, error_msg))) + { + llwarns << error_msg << ": " << in_fname << llendl; + return(format_error); + } + +#if 1 + unsigned char wav_header[44]; /*Flawfinder: ignore*/ + + S32 data_left = 0; + + LLAPRFile infile ; + infile.open(in_fname,LL_APR_RB); + if (!infile.getFileHandle()) + { + llwarns << "Couldn't open temporary ogg file for writing: " << in_fname + << llendl; + return(LLVORBISENC_SOURCE_OPEN_ERR); + } + + LLAPRFile outfile ; + outfile.open(out_fname,LL_APR_WPB); + if (!outfile.getFileHandle()) + { + llwarns << "Couldn't open upload sound file for reading: " << in_fname + << llendl; + return(LLVORBISENC_DEST_OPEN_ERR); + } + + // parse the chunks + U32 chunk_length = 0; + U32 file_pos = 12; // start at the first chunk (usually fmt but not always) + + while (infile.eof() != APR_EOF) + { + infile.seek(APR_SET,file_pos); + infile.read(wav_header, 44); + + chunk_length = ((U32) wav_header[7] << 24) + + ((U32) wav_header[6] << 16) + + ((U32) wav_header[5] << 8) + + wav_header[4]; + +// llinfos << "chunk found: '" << wav_header[0] << wav_header[1] << wav_header[2] << wav_header[3] << "'" << llendl; + + if (!(strncmp((char *)&(wav_header[0]),"fmt ",4))) + { + num_channels = ((U16) wav_header[11] << 8) + wav_header[10]; + sample_rate = ((U32) wav_header[15] << 24) + + ((U32) wav_header[14] << 16) + + ((U32) wav_header[13] << 8) + + wav_header[12]; + bits_per_sample = ((U16) wav_header[23] << 8) + wav_header[22]; + } + else if (!(strncmp((char *)&(wav_header[0]),"data",4))) + { + infile.seek(APR_SET,file_pos+8); + // leave the file pointer at the beginning of the data chunk data + data_left = chunk_length; + break; + } + file_pos += (chunk_length + 8); + chunk_length = 0; + } + + + /********** Encode setup ************/ + + /* choose an encoding mode */ + /* (mode 0: 44kHz stereo uncoupled, roughly 128kbps VBR) */ + vorbis_info_init(&vi); + + // always encode to mono + + // SL-52913 & SL-53779 determined this quality level to be our 'good + // enough' general-purpose quality level with a nice low bitrate. + // Equivalent to oggenc -q0.5 + F32 quality = 0.05f; +// quality = (bitrate==128000 ? 0.4f : 0.1); + +// if (vorbis_encode_init(&vi, /* num_channels */ 1 ,sample_rate, -1, bitrate, -1)) + if (vorbis_encode_init_vbr(&vi, /* num_channels */ 1 ,sample_rate, quality)) +// if (vorbis_encode_setup_managed(&vi,1,sample_rate,-1,bitrate,-1) || +// vorbis_encode_ctl(&vi,OV_ECTL_RATEMANAGE_AVG,NULL) || +// vorbis_encode_setup_init(&vi)) + { + llwarns << "unable to initialize vorbis codec at quality " << quality << llendl; + // llwarns << "unable to initialize vorbis codec at bitrate " << bitrate << llendl; + return(LLVORBISENC_DEST_OPEN_ERR); + } + + /* add a comment */ + vorbis_comment_init(&vc); +// vorbis_comment_add(&vc,"Linden"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init(&vd,&vi); + vorbis_block_init(&vd,&vb); + + /* set up our packet->stream encoder */ + /* pick a random serial number; that way we can more likely build + chained streams just by concatenation */ + ogg_stream_init(&os, ll_rand()); + + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code); + ogg_stream_packetin(&os,&header); /* automatically placed in its own + page */ + ogg_stream_packetin(&os,&header_comm); + ogg_stream_packetin(&os,&header_code); + + /* We don't have to write out here, but doing so makes streaming + * much easier, so we do, flushing ALL pages. This ensures the actual + * audio data will start on a new page + */ + while(!eos){ + int result=ogg_stream_flush(&os,&og); + if(result==0)break; + outfile.write(og.header, og.header_len); + outfile.write(og.body, og.body_len); + } + + } + + + while(!eos) + { + long bytes_per_sample = bits_per_sample/8; + + long bytes=(long)infile.read(readbuffer,llclamp((S32)(READ_BUFFER*num_channels*bytes_per_sample),0,data_left)); /* stereo hardwired here */ + + if (bytes==0) + { + /* end of file. this can be done implicitly in the mainline, + but it's easier to see here in non-clever fashion. + Tell the library we're at end of stream so that it can handle + the last frame and mark end of stream in the output properly */ + + vorbis_analysis_wrote(&vd,0); +// eos = 1; + + } + else + { + long i; + long samples; + int temp; + + data_left -= bytes; + /* data to encode */ + + /* expose the buffer to submit data */ + float **buffer=vorbis_analysis_buffer(&vd,READ_BUFFER); + + i = 0; + samples = bytes / (num_channels * bytes_per_sample); + + if (num_channels == 2) + { + if (bytes_per_sample == 2) + { + /* uninterleave samples */ + for(i=0; i<samples ;i++) + { + temp = ((signed char *)readbuffer)[i*4+1]; /*Flawfinder: ignore*/ + temp += ((signed char *)readbuffer)[i*4+3]; /*Flawfinder: ignore*/ + temp <<= 8; + temp += readbuffer[i*4]; + temp += readbuffer[i*4+2]; + + buffer[0][i] = ((float)temp) / 65536.f; + } + } + else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard") + { + /* uninterleave samples */ + for(i=0; i<samples ;i++) + { + temp = readbuffer[i*2+0]; + temp += readbuffer[i*2+1]; + temp -= 256; + buffer[0][i] = ((float)temp) / 256.f; + } + } + } + else if (num_channels == 1) + { + if (bytes_per_sample == 2) + { + for(i=0; i < samples ;i++) + { + temp = ((signed char*)readbuffer)[i*2+1]; + temp <<= 8; + temp += readbuffer[i*2]; + buffer[0][i] = ((float)temp) / 32768.f; + } + } + else // presume it's 1 byte per which is unsigned (F#@%ing wav "standard") + { + for(i=0; i < samples ;i++) + { + temp = readbuffer[i]; + temp -= 128; + buffer[0][i] = ((float)temp) / 128.f; + } + } + } + + /* tell the library how much we actually submitted */ + vorbis_analysis_wrote(&vd,i); + } + + /* vorbis does some data preanalysis, then divvies up blocks for + more involved (potentially parallel) processing. Get a single + block for encoding now */ + while(vorbis_analysis_blockout(&vd,&vb)==1) + { + + /* analysis */ + /* Do the main analysis, creating a packet */ + vorbis_analysis(&vb, NULL); + vorbis_bitrate_addblock(&vb); + + while(vorbis_bitrate_flushpacket(&vd, &op)) + { + + /* weld the packet into the bitstream */ + ogg_stream_packetin(&os,&op); + + /* write out pages (if any) */ + while(!eos) + { + result = ogg_stream_pageout(&os,&og); + + if(result==0) + break; + + outfile.write(og.header, og.header_len); + outfile.write(og.body, og.body_len); + + /* this could be set above, but for illustrative purposes, I do + it here (to show that vorbis does know where the stream ends) */ + + if(ogg_page_eos(&og)) + eos=1; + + } + } + } + } + + + + /* clean up and exit. vorbis_info_clear() must be called last */ + + ogg_stream_clear(&os); + vorbis_block_clear(&vb); + vorbis_dsp_clear(&vd); + vorbis_comment_clear(&vc); + vorbis_info_clear(&vi); + + /* ogg_page and ogg_packet structs always point to storage in + libvorbis. They're never freed or manipulated directly */ + +// fprintf(stderr,"Vorbis encoding: Done.\n"); + llinfos << "Vorbis encoding: Done." << llendl; + +#endif + return(LLVORBISENC_NOERR); + +} diff --git a/indra/llaudio/llvorbisencode.h b/indra/llaudio/llvorbisencode.h new file mode 100644 index 0000000000..ff5ce3a053 --- /dev/null +++ b/indra/llaudio/llvorbisencode.h @@ -0,0 +1,53 @@ +/** + * @file vorbisencode.h + * @brief Vorbis encoding routine routine for Indra. + * + * $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_VORBISENCODE_H +#define LL_VORBISENCODE_H + +const S32 LLVORBISENC_NOERR = 0; // no error +const S32 LLVORBISENC_SOURCE_OPEN_ERR = 1; // error opening source +const S32 LLVORBISENC_DEST_OPEN_ERR = 2; // error opening destination +const S32 LLVORBISENC_WAV_FORMAT_ERR = 3; // not a WAV +const S32 LLVORBISENC_PCM_FORMAT_ERR = 4; // not a PCM +const S32 LLVORBISENC_MONO_ERR = 5; // can't do mono +const S32 LLVORBISENC_STEREO_ERR = 6; // can't do stereo +const S32 LLVORBISENC_MULTICHANNEL_ERR = 7; // can't do stereo +const S32 LLVORBISENC_UNSUPPORTED_SAMPLE_RATE = 8; // unsupported sample rate +const S32 LLVORBISENC_UNSUPPORTED_WORD_SIZE = 9; // unsupported word size +const S32 LLVORBISENC_CLIP_TOO_LONG = 10; // source file is too long + + +S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& error_msg); +S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname); + +#endif + diff --git a/indra/llaudio/llwindgen.h b/indra/llaudio/llwindgen.h new file mode 100644 index 0000000000..847bfa6e9d --- /dev/null +++ b/indra/llaudio/llwindgen.h @@ -0,0 +1,136 @@ +/** + * @file windgen.h + * @brief Templated wind noise generation + * + * $LicenseInfo:firstyear=2002&license=viewergpl$ + * + * Copyright (c) 2002-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 WINDGEN_H +#define WINDGEN_H + +#include "llcommon.h" +#include "llrand.h" + +template <class MIXBUFFERFORMAT_T> +class LLWindGen +{ +public: + LLWindGen() : + mTargetGain(0.f), + mTargetFreq(100.f), + mTargetPanGainR(0.5f), + mbuf0(0.0), + mbuf1(0.0), + mbuf2(0.0), + mbuf3(0.0), + mbuf4(0.0), + mbuf5(0.0), + mY0(0.0), + mY1(0.0), + mCurrentGain(0.f), + mCurrentFreq(100.f), + mCurrentPanGainR(0.5f) {}; + + static const U32 getInputSamplingRate() {return mInputSamplingRate;} + + // newbuffer = the buffer passed from the previous DSP unit. + // numsamples = length in samples-per-channel at this mix time. + // stride = number of bytes between start of each sample. + // NOTE: generates L/R interleaved stereo + MIXBUFFERFORMAT_T* windGenerate(MIXBUFFERFORMAT_T *newbuffer, int numsamples, int stride) + { + U8 *cursamplep = (U8*)newbuffer; + + double bandwidth = 50.0F; + double a0,b1,b2; + + // calculate resonant filter coeffs + b2 = exp(-(F_TWO_PI) * (bandwidth / mInputSamplingRate)); + + while (numsamples--) + { + mCurrentFreq = (float)((0.999 * mCurrentFreq) + (0.001 * mTargetFreq)); + mCurrentGain = (float)((0.999 * mCurrentGain) + (0.001 * mTargetGain)); + mCurrentPanGainR = (float)((0.999 * mCurrentPanGainR) + (0.001 * mTargetPanGainR)); + b1 = (-4.0 * b2) / (1.0 + b2) * cos(F_TWO_PI * (mCurrentFreq / mInputSamplingRate)); + a0 = (1.0 - b2) * sqrt(1.0 - (b1 * b1) / (4.0 * b2)); + double nextSample; + + // start with white noise + nextSample = ll_frand(2.0f) - 1.0f; + + // apply pinking filter + mbuf0 = 0.997f * mbuf0 + 0.0126502f * nextSample; + mbuf1 = 0.985f * mbuf1 + 0.0139083f * nextSample; + mbuf2 = 0.950f * mbuf2 + 0.0205439f * nextSample; + mbuf3 = 0.850f * mbuf3 + 0.0387225f * nextSample; + mbuf4 = 0.620f * mbuf4 + 0.0465932f * nextSample; + mbuf5 = 0.250f * mbuf5 + 0.1093477f * nextSample; + + nextSample = mbuf0 + mbuf1 + mbuf2 + mbuf3 + mbuf4 + mbuf5; + + // do a resonant filter on the noise + nextSample = (double)( a0 * nextSample - b1 * mY0 - b2 * mY1 ); + mY1 = mY0; + mY0 = nextSample; + + nextSample *= mCurrentGain; + + MIXBUFFERFORMAT_T sample; + + sample = llfloor(((F32)nextSample*32768.f*(1.0f - mCurrentPanGainR))+0.5f); + *(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767); + cursamplep += stride; + + sample = llfloor(((F32)nextSample*32768.f*mCurrentPanGainR)+0.5f); + *(MIXBUFFERFORMAT_T*)cursamplep = llclamp(sample, (MIXBUFFERFORMAT_T)-32768, (MIXBUFFERFORMAT_T)32767); + cursamplep += stride; + } + + return newbuffer; + } + + F32 mTargetGain; + F32 mTargetFreq; + F32 mTargetPanGainR; + +private: + static const U32 mInputSamplingRate = 44100; + F64 mbuf0; + F64 mbuf1; + F64 mbuf2; + F64 mbuf3; + F64 mbuf4; + F64 mbuf5; + F64 mY0; + F64 mY1; + F32 mCurrentGain; + F32 mCurrentFreq; + F32 mCurrentPanGainR; +}; + +#endif diff --git a/indra/llcharacter/llcharacter.cpp b/indra/llcharacter/llcharacter.cpp index 6633c65317..20ff7bab34 100644 --- a/indra/llcharacter/llcharacter.cpp +++ b/indra/llcharacter/llcharacter.cpp @@ -180,9 +180,11 @@ void LLCharacter::requestStopMotion( LLMotion* motion) //----------------------------------------------------------------------------- // updateMotions() //----------------------------------------------------------------------------- +static LLFastTimer::DeclareTimer FTM_UPDATE_ANIMATION("Update Animation"); + void LLCharacter::updateMotions(e_update_t update_type) { - LLFastTimer t(LLFastTimer::FTM_UPDATE_ANIMATION); + LLFastTimer t(FTM_UPDATE_ANIMATION); if (update_type == HIDDEN_UPDATE) { mMotionController.updateMotionsMinimal(); diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 7fe4491a8b..af573a7e6c 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -64,6 +64,7 @@ set(llcommon_SOURCE_FILES llmetrics.cpp llmortician.cpp llptrto.cpp + llprocesslauncher.cpp llprocessor.cpp llqueuedthread.cpp llrand.cpp @@ -176,6 +177,7 @@ set(llcommon_HEADER_FILES llpointer.h llpreprocessor.h llpriqueuemap.h + llprocesslauncher.h llprocessor.h llptrskiplist.h llptrskipmap.h diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index 835cdbca04..e595cc1f8b 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -101,7 +101,7 @@ LLAssetDictionary::LLAssetDictionary() addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", "Uncompressed Images", DAD_NONE, FALSE, TRUE)); addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", "Uncompressed Images", DAD_NONE, FALSE, TRUE)); addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", "Animations", DAD_ANIMATION, FALSE, TRUE)); - addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", "Gestures", DAD_GESTURE, FALSE, TRUE)); + addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", "Gestures", DAD_GESTURE, TRUE, TRUE)); addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", "New Folder", DAD_NONE, FALSE, TRUE)); addEntry(LLAssetType::AT_FAVORITE, new AssetEntry("FAVORITE", "favorite", "favorite", "favorite", DAD_NONE, FALSE, TRUE)); @@ -116,7 +116,7 @@ LLAssetDictionary::LLAssetDictionary() } addEntry(LLAssetType::AT_CURRENT_OUTFIT, new AssetEntry("CURRENT", "current", "current outfit", "Current Outfit", DAD_CATEGORY, FALSE, TRUE)); - addEntry(LLAssetType::AT_OUTFIT, new AssetEntry("OUTFIT", "outfit", "outfit", "Outfit", DAD_CATEGORY, TRUE, FALSE)); + addEntry(LLAssetType::AT_OUTFIT, new AssetEntry("OUTFIT", "outfit", "outfit", "New Outfit", DAD_CATEGORY, TRUE, FALSE)); addEntry(LLAssetType::AT_MY_OUTFITS, new AssetEntry("MY_OUTFITS", "my_otfts", "my outfits", "My Outfits", DAD_CATEGORY, FALSE, TRUE)); addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, "New Folder", DAD_NONE, FALSE, FALSE)); diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 1a559ed7e0..c84f5d0fd7 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -73,6 +73,7 @@ namespace LLError LL_COMMON_API void setFunctionLevel(const std::string& function_name, LLError::ELevel); LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel); LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel); + void setTagLevel(const std::string& file_name, LLError::ELevel); LL_COMMON_API void configure(const LLSD&); // the LLSD can configure all of the settings diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 0488585fd4..6ea9762546 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -69,13 +69,13 @@ public: std::vector<NamedTimer*>& getChildren();
void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
- bool getCollapsed() { return mCollapsed; }
+ bool getCollapsed() const { return mCollapsed; }
- U64 getCountAverage() { return mCountAverage; }
- U64 getCallAverage() { return mCallAverage; }
+ U64 getCountAverage() const { return mCountAverage; }
+ U64 getCallAverage() const { return mCallAverage; }
- U64 getHistoricalCount(S32 history_index = 0);
- U64 getHistoricalCalls(S32 history_index = 0);
+ U64 getHistoricalCount(S32 history_index = 0) const;
+ U64 getHistoricalCalls(S32 history_index = 0) const;
static NamedTimer& getRootNamedTimer();
@@ -162,117 +162,6 @@ public: NamedTimer& mNamedTimer;
};
- static DeclareTimer FTM_ARRANGE;
- static DeclareTimer FTM_ATTACHMENT_UPDATE;
- static DeclareTimer FTM_AUDIO_UPDATE;
- static DeclareTimer FTM_AUTO_SELECT;
- static DeclareTimer FTM_AVATAR_UPDATE;
- static DeclareTimer FTM_CLEANUP;
- static DeclareTimer FTM_CLIENT_COPY;
- static DeclareTimer FTM_CREATE_OBJECT;
- static DeclareTimer FTM_CULL;
- static DeclareTimer FTM_CULL_REBOUND;
- static DeclareTimer FTM_FILTER;
- static DeclareTimer FTM_FLEXIBLE_UPDATE;
- static DeclareTimer FTM_FRAME;
- static DeclareTimer FTM_FRUSTUM_CULL;
- static DeclareTimer FTM_GEN_FLEX;
- static DeclareTimer FTM_GEN_TRIANGLES;
- static DeclareTimer FTM_GEN_VOLUME;
- static DeclareTimer FTM_GEO_SKY;
- static DeclareTimer FTM_GEO_UPDATE;
- static DeclareTimer FTM_HUD_EFFECTS;
- static DeclareTimer FTM_HUD_UPDATE;
- static DeclareTimer FTM_IDLE;
- static DeclareTimer FTM_IDLE_CB;
- static DeclareTimer FTM_IDLE_NETWORK;
- static DeclareTimer FTM_IMAGE_CREATE;
- static DeclareTimer FTM_IMAGE_MARK_DIRTY;
- static DeclareTimer FTM_IMAGE_UPDATE;
- static DeclareTimer FTM_INVENTORY;
- static DeclareTimer FTM_JOINT_UPDATE;
- static DeclareTimer FTM_KEYHANDLER;
- static DeclareTimer FTM_LOAD_AVATAR;
- static DeclareTimer FTM_LOD_UPDATE;
- static DeclareTimer FTM_MESSAGES;
- static DeclareTimer FTM_MOUSEHANDLER;
- static DeclareTimer FTM_NETWORK;
- static DeclareTimer FTM_OBJECTLIST_UPDATE;
- static DeclareTimer FTM_OCCLUSION_READBACK;
- static DeclareTimer FTM_OCTREE_BALANCE;
- static DeclareTimer FTM_PICK;
- static DeclareTimer FTM_PIPELINE;
- static DeclareTimer FTM_POOLRENDER;
- static DeclareTimer FTM_POOLS;
- static DeclareTimer FTM_PROCESS_IMAGES;
- static DeclareTimer FTM_PROCESS_MESSAGES;
- static DeclareTimer FTM_PROCESS_OBJECTS;
- static DeclareTimer FTM_PUMP;
- static DeclareTimer FTM_REBUILD_GRASS_VB;
- static DeclareTimer FTM_REBUILD_PARTICLE_VB;
- static DeclareTimer FTM_REBUILD_TERRAIN_VB;
- static DeclareTimer FTM_REBUILD_VBO;
- static DeclareTimer FTM_REBUILD_VOLUME_VB;
- static DeclareTimer FTM_REFRESH;
- static DeclareTimer FTM_REGION_UPDATE;
- static DeclareTimer FTM_RENDER;
- static DeclareTimer FTM_RENDER_ALPHA;
- static DeclareTimer FTM_RENDER_BLOOM;
- static DeclareTimer FTM_RENDER_BLOOM_FBO;
- static DeclareTimer FTM_RENDER_BUMP;
- static DeclareTimer FTM_RENDER_CHARACTERS;
- static DeclareTimer FTM_RENDER_FAKE_VBO_UPDATE;
- static DeclareTimer FTM_RENDER_FONTS;
- static DeclareTimer FTM_RENDER_FULLBRIGHT;
- static DeclareTimer FTM_RENDER_GEOMETRY;
- static DeclareTimer FTM_RENDER_GLOW;
- static DeclareTimer FTM_RENDER_GRASS;
- static DeclareTimer FTM_RENDER_INVISIBLE;
- static DeclareTimer FTM_RENDER_OCCLUSION;
- static DeclareTimer FTM_RENDER_SHINY;
- static DeclareTimer FTM_RENDER_SIMPLE;
- static DeclareTimer FTM_RENDER_TERRAIN;
- static DeclareTimer FTM_RENDER_TREES;
- static DeclareTimer FTM_RENDER_UI;
- static DeclareTimer FTM_RENDER_WATER;
- static DeclareTimer FTM_RENDER_WL_SKY;
- static DeclareTimer FTM_RESET_DRAWORDER;
- static DeclareTimer FTM_SHADOW_ALPHA;
- static DeclareTimer FTM_SHADOW_AVATAR;
- static DeclareTimer FTM_SHADOW_RENDER;
- static DeclareTimer FTM_SHADOW_SIMPLE;
- static DeclareTimer FTM_SHADOW_TERRAIN;
- static DeclareTimer FTM_SHADOW_TREE;
- static DeclareTimer FTM_SIMULATE_PARTICLES;
- static DeclareTimer FTM_SLEEP;
- static DeclareTimer FTM_SORT;
- static DeclareTimer FTM_STATESORT;
- static DeclareTimer FTM_STATESORT_DRAWABLE;
- static DeclareTimer FTM_STATESORT_POSTSORT;
- static DeclareTimer FTM_SWAP;
- static DeclareTimer FTM_TEMP1;
- static DeclareTimer FTM_TEMP2;
- static DeclareTimer FTM_TEMP3;
- static DeclareTimer FTM_TEMP4;
- static DeclareTimer FTM_TEMP5;
- static DeclareTimer FTM_TEMP6;
- static DeclareTimer FTM_TEMP7;
- static DeclareTimer FTM_TEMP8;
- static DeclareTimer FTM_UPDATE_ANIMATION;
- static DeclareTimer FTM_UPDATE_AVATAR;
- static DeclareTimer FTM_UPDATE_CLOUDS;
- static DeclareTimer FTM_UPDATE_GRASS;
- static DeclareTimer FTM_UPDATE_MOVE;
- static DeclareTimer FTM_UPDATE_PARTICLES;
- static DeclareTimer FTM_UPDATE_PRIMITIVES;
- static DeclareTimer FTM_UPDATE_SKY;
- static DeclareTimer FTM_UPDATE_TERRAIN;
- static DeclareTimer FTM_UPDATE_TEXTURES;
- static DeclareTimer FTM_UPDATE_TREE;
- static DeclareTimer FTM_UPDATE_WATER;
- static DeclareTimer FTM_UPDATE_WLPARAM;
- static DeclareTimer FTM_VFILE_WAIT;
- static DeclareTimer FTM_WORLD_UPDATE;
public:
enum RootTimerMarker { ROOT };
@@ -287,6 +176,7 @@ public: LLFastTimer(NamedTimer::FrameState& timer)
: mFrameState(&timer)
{
+#if FAST_TIMER_ON
NamedTimer::FrameState* frame_state = mFrameState;
frame_state->mLastStartTime = get_cpu_clock_count();
mStartSelfTime = frame_state->mLastStartTime;
@@ -298,6 +188,7 @@ public: mLastTimer = sCurTimer;
sCurTimer = this;
+#endif
}
~LLFastTimer()
@@ -332,6 +223,7 @@ public: static S32 getCurFrameIndex() { return sCurFrameIndex; }
static void writeLog(std::ostream& os);
+ static const NamedTimer* getTimerByName(const std::string& name);
public:
static bool sPauseHistory;
diff --git a/indra/llcommon/llmetrics.cpp b/indra/llcommon/llmetrics.cpp index 8db3284c42..30e5d435ae 100644 --- a/indra/llcommon/llmetrics.cpp +++ b/indra/llcommon/llmetrics.cpp @@ -71,7 +71,7 @@ void LLMetricsImpl::recordEventDetails(const std::string& location, metrics["location"] = location; metrics["stats"] = stats; - llinfos << "LLMETRICS: " << LLSDNotationStreamer(metrics) << llendl; + llinfos << "LLMETRICS: " << (LLSDNotationStreamer(metrics)) << llendl; } // Store this: diff --git a/indra/llcommon/llprocesslauncher.cpp b/indra/llcommon/llprocesslauncher.cpp new file mode 100644 index 0000000000..f0315e92eb --- /dev/null +++ b/indra/llcommon/llprocesslauncher.cpp @@ -0,0 +1,344 @@ +/** + * @file llprocesslauncher.cpp + * @brief Utility class for launching, terminating, and tracking the state of processes. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llprocesslauncher.h" + +#include <iostream> +#if LL_DARWIN || LL_LINUX +// not required or present on Win32 +#include <sys/wait.h> +#endif + +LLProcessLauncher::LLProcessLauncher() +{ +#if LL_WINDOWS + mProcessHandle = 0; +#else + mProcessID = 0; +#endif +} + +LLProcessLauncher::~LLProcessLauncher() +{ + kill(); +} + +void LLProcessLauncher::setExecutable(const std::string &executable) +{ + mExecutable = executable; +} + +void LLProcessLauncher::setWorkingDirectory(const std::string &dir) +{ + mWorkingDir = dir; +} + +void LLProcessLauncher::clearArguments() +{ + mLaunchArguments.clear(); +} + +void LLProcessLauncher::addArgument(const std::string &arg) +{ + mLaunchArguments.push_back(arg); +} + +void LLProcessLauncher::addArgument(const char *arg) +{ + mLaunchArguments.push_back(std::string(arg)); +} + +#if LL_WINDOWS + +int LLProcessLauncher::launch(void) +{ + // If there was already a process associated with this object, kill it. + kill(); + orphan(); + + int result = 0; + + PROCESS_INFORMATION pinfo; + STARTUPINFOA sinfo; + memset(&sinfo, 0, sizeof(sinfo)); + + std::string args = mExecutable; + for(int i = 0; i < (int)mLaunchArguments.size(); i++) + { + args += " "; + args += mLaunchArguments[i]; + } + + // So retarded. Windows requires that the second parameter to CreateProcessA be a writable (non-const) string... + char *args2 = new char[args.size() + 1]; + strcpy(args2, args.c_str()); + + if( ! CreateProcessA( NULL, args2, NULL, NULL, FALSE, 0, NULL, NULL, &sinfo, &pinfo ) ) + { + // TODO: do better than returning the OS-specific error code on failure... + result = GetLastError(); + if(result == 0) + { + // Make absolutely certain we return a non-zero value on failure. + result = -1; + } + } + else + { + // foo = pinfo.dwProcessId; // get your pid here if you want to use it later on + // CloseHandle(pinfo.hProcess); // stops leaks - nothing else + mProcessHandle = pinfo.hProcess; + CloseHandle(pinfo.hThread); // stops leaks - nothing else + } + + delete[] args2; + + return result; +} + +bool LLProcessLauncher::isRunning(void) +{ + if(mProcessHandle != 0) + { + DWORD waitresult = WaitForSingleObject(mProcessHandle, 0); + if(waitresult == WAIT_OBJECT_0) + { + // the process has completed. + mProcessHandle = 0; + } + } + + return (mProcessHandle != 0); +} +bool LLProcessLauncher::kill(void) +{ + bool result = true; + + if(mProcessHandle != 0) + { + TerminateProcess(mProcessHandle,0); + + if(isRunning()) + { + result = false; + } + } + + return result; +} + +void LLProcessLauncher::orphan(void) +{ + // Forget about the process + mProcessHandle = 0; +} + +// static +void LLProcessLauncher::reap(void) +{ + // No actions necessary on Windows. +} + +#else // Mac and linux + +#include <signal.h> +#include <fcntl.h> +#include <errno.h> + +static std::list<pid_t> sZombies; + +// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise. +static bool reap_pid(pid_t pid) +{ + bool result = false; + + pid_t wait_result = ::waitpid(pid, NULL, WNOHANG); + if(wait_result == pid) + { + result = true; + } + else if(wait_result == -1) + { + if(errno == ECHILD) + { + // No such process -- this may mean we're ignoring SIGCHILD. + result = true; + } + } + + return result; +} + +int LLProcessLauncher::launch(void) +{ + // If there was already a process associated with this object, kill it. + kill(); + orphan(); + + int result = 0; + int current_wd = -1; + + // create an argv vector for the child process + const char ** fake_argv = new const char *[mLaunchArguments.size() + 2]; // 1 for the executable path, 1 for the NULL terminator + + int i = 0; + + // add the executable path + fake_argv[i++] = mExecutable.c_str(); + + // and any arguments + for(int j=0; j < mLaunchArguments.size(); j++) + fake_argv[i++] = mLaunchArguments[j].c_str(); + + // terminate with a null pointer + fake_argv[i] = NULL; + + if(!mWorkingDir.empty()) + { + // save the current working directory + current_wd = ::open(".", O_RDONLY); + + // and change to the one the child will be executed in + if (::chdir(mWorkingDir.c_str())) + { + // chdir failed + } + } + + // flush all buffers before the child inherits them + ::fflush(NULL); + + pid_t id = vfork(); + if(id == 0) + { + // child process + + ::execv(mExecutable.c_str(), (char * const *)fake_argv); + + // If we reach this point, the exec failed. + // Use _exit() instead of exit() per the vfork man page. + _exit(0); + } + + // parent process + + if(current_wd >= 0) + { + // restore the previous working directory + if (::fchdir(current_wd)) + { + // chdir failed + } + ::close(current_wd); + } + + delete[] fake_argv; + + mProcessID = id; + + // At this point, the child process will have been created (since that's how vfork works -- the child borrowed our execution context until it forked) + // If the process doesn't exist at this point, the exec failed. + if(!isRunning()) + { + result = -1; + } + + return result; +} + +bool LLProcessLauncher::isRunning(void) +{ + if(mProcessID != 0) + { + // Check whether the process has exited, and reap it if it has. + if(reap_pid(mProcessID)) + { + // the process has exited. + mProcessID = 0; + } + } + + return (mProcessID != 0); +} + +bool LLProcessLauncher::kill(void) +{ + bool result = true; + + if(mProcessID != 0) + { + // Try to kill the process. We'll do approximately the same thing whether the kill returns an error or not, so we ignore the result. + (void)::kill(mProcessID, SIGTERM); + + // This will have the side-effect of reaping the zombie if the process has exited. + if(isRunning()) + { + result = false; + } + } + + return result; +} + +void LLProcessLauncher::orphan(void) +{ + // Disassociate the process from this object + if(mProcessID != 0) + { + // We may still need to reap the process's zombie eventually + sZombies.push_back(mProcessID); + + mProcessID = 0; + } +} + +// static +void LLProcessLauncher::reap(void) +{ + // Attempt to real all saved process ID's. + + std::list<pid_t>::iterator iter = sZombies.begin(); + while(iter != sZombies.end()) + { + if(reap_pid(*iter)) + { + iter = sZombies.erase(iter); + } + else + { + iter++; + } + } +} + +#endif diff --git a/indra/llcommon/llprocesslauncher.h b/indra/llcommon/llprocesslauncher.h new file mode 100644 index 0000000000..a1b8e22691 --- /dev/null +++ b/indra/llcommon/llprocesslauncher.h @@ -0,0 +1,85 @@ +/** + * @file llprocesslauncher.h + * @brief Utility class for launching, terminating, and tracking the state of processes. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPROCESSLAUNCHER_H +#define LL_LLPROCESSLAUNCHER_H + +#if LL_WINDOWS +#include <windows.h> +#endif + + +/* + LLProcessLauncher handles launching external processes with specified command line arguments. + It also keeps track of whether the process is still running, and can kill it if required. +*/ + +class LLProcessLauncher +{ + LOG_CLASS(LLProcessLauncher); +public: + LLProcessLauncher(); + virtual ~LLProcessLauncher(); + + void setExecutable(const std::string &executable); + void setWorkingDirectory(const std::string &dir); + + void clearArguments(); + void addArgument(const std::string &arg); + void addArgument(const char *arg); + + int launch(void); + bool isRunning(void); + + // Attempt to kill the process -- returns true if the process is no longer running when it returns. + // Note that even if this returns false, the process may exit some time after it's called. + bool kill(void); + + // Use this if you want the external process to continue execution after the LLProcessLauncher instance controlling it is deleted. + // Normally, the destructor will attempt to kill the process and wait for termination. + // This should only be used if the viewer is about to exit -- otherwise, the child process will become a zombie after it exits. + void orphan(void); + + // This needs to be called periodically on Mac/Linux to clean up zombie processes. + static void reap(void); +private: + std::string mExecutable; + std::string mWorkingDir; + std::vector<std::string> mLaunchArguments; + +#if LL_WINDOWS + HANDLE mProcessHandle; +#else + pid_t mProcessID; +#endif +}; + +#endif // LL_LLPROCESSLAUNCHER_H diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 5f102509fd..9ab844eb22 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -32,6 +32,8 @@ #ifndef LLREFCOUNT_H #define LLREFCOUNT_H +#include <boost/noncopyable.hpp> + //---------------------------------------------------------------------------- // RefCount objects should generally only be accessed by way of LLPointer<>'s // see llthread.h for LLThreadSafeRefCount @@ -40,10 +42,9 @@ class LL_COMMON_API LLRefCount { private: - LLRefCount(const LLRefCount&); // not implemented + LLRefCount(const LLRefCount& other); // no implementation private: - LLRefCount&operator=(const LLRefCount&); // not implemented - + LLRefCount& operator=(const LLRefCount&); // no implementation protected: virtual ~LLRefCount(); // use unref() diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index ba0f798dbb..9140ebb3f3 100644 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -819,9 +819,15 @@ static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat) { std::ostringstream out; if (useXMLFormat) - out << LLSDXMLStreamer(llsd); + { + LLSDXMLStreamer xml_streamer(llsd); + out << xml_streamer; + } else - out << LLSDNotationStreamer(llsd); + { + LLSDNotationStreamer notation_streamer(llsd); + out << notation_streamer; + } out_string = out.str(); } int len = out_string.length(); diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index ec1718a8cb..d7da40d645 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -42,6 +42,9 @@ #include <winnls.h> // for WideCharToMultiByte #endif +LLFastTimer::DeclareTimer STRING_LOCALIZATION("String Localization"); + + std::string ll_safe_string(const char* in) { if(in) return std::string(in); diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index 24a8d49a54..f5c6b297b9 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -38,6 +38,7 @@ #include <iomanip>
#include <boost/regex.hpp>
#include "llsd.h"
+#include "llfasttimer.h"
#if LL_LINUX || LL_SOLARIS
#include <wctype.h>
@@ -232,9 +233,10 @@ public: typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
static void getTokens (std::basic_string<T> input, std::vector<std::basic_string<T> >& tokens);
static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
- static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, const LLSD& substitutions);
+ static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
+ static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
- static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map);
+ static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
static void setLocale (std::string inLocale) {sLocale = inLocale;};
static std::string getLocale (void) {return sLocale;};
@@ -336,6 +338,9 @@ public: // Copies src into dst at a given offset.
static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
+ static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
+
+
#ifdef _DEBUG
static void testHarness();
#endif
@@ -623,26 +628,89 @@ void LLStringUtilBase<T>::getTokens (std::basic_string<T> input, std::vector<std }
}
+extern LLFastTimer::DeclareTimer STRING_LOCALIZATION;
+
// static
template<class T>
-S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& fmt_map)
+S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const format_map_t& substitutions)
{
- LLSD llsdMap;
+ LLFastTimer ft(STRING_LOCALIZATION);
+ S32 res = 0;
- for (format_map_t::const_iterator iter = fmt_map.begin();
- iter != fmt_map.end();
- ++iter)
+ std::basic_ostringstream<T> output;
+ // match strings like [NAME,number,3]
+ const boost::regex key("\\[((\\s)*([0-9_A-Za-z]+)((\\s)*,(\\s)*[0-9_A-Za-z\\s]*){0,2}(\\s)*)]");
+
+
+ typename std::basic_string<T>::const_iterator start = s.begin();
+ typename std::basic_string<T>::const_iterator end = s.end();
+ boost::smatch match;
+
+
+ while (boost::regex_search(start, end, match, key, boost::match_default))
{
- llsdMap[iter->first] = iter->second;
- }
+ bool found_replacement = false;
+ std::vector<std::basic_string<T> > tokens;
+ std::basic_string<T> replacement;
+
+ getTokens (std::basic_string<T>(match[1].first, match[1].second), tokens);
+
+ if (tokens.size() == 1)
+ {
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ }
+ else if (tokens[1] == "number")
+ {
+ std::basic_string<T> param = "0";
+
+ if (tokens.size() > 2) param = tokens[2];
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ if (found_replacement) formatNumber (replacement, param);
+ }
+ else if (tokens[1] == "datetime")
+ {
+ std::basic_string<T> param;
+ if (tokens.size() > 2) param = tokens[2];
+
+ format_map_t::const_iterator iter = substitutions.find("datetime");
+ if (iter != substitutions.end())
+ {
+ S32 secFromEpoch = 0;
+ BOOL r = LLStringUtil::convertToS32(iter->second, secFromEpoch);
+ if (r)
+ {
+ found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch);
+ }
+ }
+ }
- return format (s, llsdMap);
+ if (found_replacement)
+ {
+ output << std::basic_string<T>(start, match[0].first) << replacement;
+ res++;
+ }
+ else
+ {
+ // we had no replacement, so leave the string we searched for so that it gets noticed by QA
+ // "hello [NAME_NOT_FOUND]" is output
+ output << std::basic_string<T>(start, match[0].second);
+ }
+
+ // update search position
+ start = match[0].second;
+ }
+ // send the remainder of the string (with no further matches for bracketed names)
+ output << std::basic_string<T>(start, end);
+ s = output.str();
+ return res;
}
//static
template<class T>
S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutions)
{
+ LLFastTimer ft(STRING_LOCALIZATION);
+
S32 res = 0;
if (!substitutions.isMap())
@@ -684,8 +752,9 @@ S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutio {
std::basic_string<T> param;
if (tokens.size() > 2) param = tokens[2];
-
- found_replacement = formatDatetime (replacement, tokens[0], param, substitutions);
+
+ S32 secFromEpoch = (S32) substitutions["datetime"].asInteger();
+ found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch);
}
if (found_replacement)
@@ -711,7 +780,32 @@ S32 LLStringUtilBase<T>::format(std::basic_string<T>& s, const LLSD& substitutio // static
template<class T>
-bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const LLSD &substitutions)
+bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const format_map_t& substitutions)
+{
+ // see if we have a replacement for the bracketed string (without the brackets)
+ // test first using has() because if we just look up with operator[] we get back an
+ // empty string even if the value is missing. We want to distinguish between
+ // missing replacements and deliberately empty replacement strings.
+ format_map_t::const_iterator iter = substitutions.find(token);
+ if (iter != substitutions.end())
+ {
+ replacement = iter->second;
+ return true;
+ }
+ // if not, see if there's one WITH brackets
+ iter = substitutions.find(std::basic_string<T>("[" + token + "]"));
+ if (iter != substitutions.end())
+ {
+ replacement = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::simpleReplacement(std::basic_string<T> &replacement, std::basic_string<T> token, const LLSD& substitutions)
{
// see if we have a replacement for the bracketed string (without the brackets)
// test first using has() because if we just look up with operator[] we get back an
@@ -771,10 +865,8 @@ void LLStringUtilBase<T>::formatNumber(std::basic_string<T>& numStr, std::basic_ // static
template<class T>
bool LLStringUtilBase<T>::formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token,
- std::basic_string<T> param, const LLSD& substitutions)
+ std::basic_string<T> param, S32 secFromEpoch)
{
- S32 secFromEpoch = (long) substitutions["datetime"].asInteger();
-
if (param == "local") // local
{
secFromEpoch -= LLStringOps::getLocalTimeOffset();
diff --git a/indra/llinventory/llparcel.cpp b/indra/llinventory/llparcel.cpp index 8c9dfd6f39..2808effdc6 100644 --- a/indra/llinventory/llparcel.cpp +++ b/indra/llinventory/llparcel.cpp @@ -199,6 +199,12 @@ void LLParcel::init(const LLUUID &owner_id, mObscureMusic = 1; mMediaWidth = 0; mMediaHeight = 0; + setMediaCurrentURL(LLStringUtil::null); + mMediaURLFilterEnable = FALSE; + mMediaURLFilterList = LLSD::emptyArray(); + mMediaAllowNavigate = TRUE; + mMediaURLTimeout = 0.0f; + mMediaPreventCameraZoom = FALSE; mGroupID.setNull(); @@ -314,6 +320,56 @@ void LLParcel::setMediaHeight(S32 height) { mMediaHeight = height; } + +void LLParcel::setMediaCurrentURL(const std::string& url) +{ + mMediaCurrentURL = url; + // The escaping here must match the escaping in the database + // abstraction layer if it's ever added. + // This should really filter the url in some way. Other than + // simply requiring non-printable. + LLStringFn::replace_nonprintable_in_ascii(mMediaCurrentURL, LL_UNKNOWN_CHAR); + +} + +void LLParcel::setMediaURLResetTimer(F32 time) +{ + mMediaResetTimer.start(); + mMediaResetTimer.setTimerExpirySec(time); +} + +void LLParcel::setMediaURLFilterList(LLSD list) +{ + // sanity check LLSD + // must be array of strings + if (!list.isArray()) + { + return; + } + + for (S32 i = 0; i < list.size(); i++) + { + if (!list[i].isString()) + return; + } + + // can't be too big + const S32 MAX_SIZE = 50; + if (list.size() > MAX_SIZE) + { + LLSD new_list = LLSD::emptyArray(); + + for (S32 i = 0; i < llmin(list.size(), MAX_SIZE); i++) + { + new_list.append(list[i]); + } + + list = new_list; + } + + mMediaURLFilterList = list; +} + // virtual void LLParcel::setLocalID(S32 local_id) { @@ -568,6 +624,34 @@ BOOL LLParcel::importAccessEntry(std::istream& input_stream, LLAccessEntry* entr return input_stream.good(); } +BOOL LLParcel::importMediaURLFilter(std::istream& input_stream, std::string& url) +{ + skip_to_end_of_next_keyword("{", input_stream); + + while(input_stream.good()) + { + skip_comments_and_emptyspace(input_stream); + std::string line, keyword, value; + get_line(line, input_stream, MAX_STRING); + get_keyword_and_value(keyword, value, line); + + if ("}" == keyword) + { + break; + } + else if ("url" == keyword) + { + url = value; + } + else + { + llwarns << "Unknown keyword in parcel media url filter section: <" + << keyword << ">" << llendl; + } + } + return input_stream.good(); +} + // Assumes we are in a block "ParcelData" void LLParcel::packMessage(LLMessageSystem* msg) { @@ -593,6 +677,7 @@ void LLParcel::packMessage(LLMessageSystem* msg) // Assumes we are in a block "ParcelData" void LLParcel::packMessage(LLSD& msg) { + // used in the viewer, the sim uses it's own packer msg["local_id"] = getLocalID(); msg["parcel_flags"] = ll_sd_from_U32(getParcelFlags()); msg["sale_price"] = getSalePrice(); @@ -606,9 +691,15 @@ void LLParcel::packMessage(LLSD& msg) msg["media_height"] = getMediaHeight(); msg["auto_scale"] = getMediaAutoScale(); msg["media_loop"] = getMediaLoop(); + msg["media_current_url"] = getMediaCurrentURL(); msg["obscure_media"] = getObscureMedia(); msg["obscure_music"] = getObscureMusic(); msg["media_id"] = getMediaID(); + msg["media_allow_navigate"] = getMediaAllowNavigate(); + msg["media_prevent_camera_zoom"] = getMediaPreventCameraZoom(); + msg["media_url_timeout"] = getMediaURLTimeout(); + msg["media_url_filter_enable"] = getMediaURLFilterEnable(); + msg["media_url_filter_list"] = getMediaURLFilterList(); msg["group_id"] = getGroupID(); msg["pass_price"] = mPassPrice; msg["pass_hours"] = mPassHours; @@ -678,6 +769,21 @@ void LLParcel::unpackMessage(LLMessageSystem* msg) mObscureMedia = true; mObscureMusic = true; } + + if(msg->getNumberOfBlocks("MediaLinkSharing") > 0) + { + msg->getString("MediaLinkSharing", "MediaCurrentURL", buffer); + setMediaCurrentURL(buffer); + msg->getU8 ( "MediaLinkSharing", "MediaAllowNavigate", mMediaAllowNavigate ); + msg->getU8 ( "MediaLinkSharing", "MediaURLFilterEnable", mMediaURLFilterEnable ); + msg->getU8 ( "MediaLinkSharing", "MediaPreventCameraZoom", mMediaPreventCameraZoom ); + msg->getF32( "MediaLinkSharing", "MediaURLTimeout", mMediaURLTimeout); + } + else + { + setMediaCurrentURL(LLStringUtil::null); + } + } void LLParcel::packAccessEntries(LLMessageSystem* msg, @@ -994,6 +1100,20 @@ BOOL LLParcel::isSaleTimerExpired(const U64& time) return expired; } +BOOL LLParcel::isMediaResetTimerExpired(const U64& time) +{ + if (mMediaResetTimer.getStarted() == FALSE) + { + return FALSE; + } + BOOL expired = mMediaResetTimer.checkExpirationAndReset(0.0); + if (expired) + { + mMediaResetTimer.stop(); + } + return expired; +} + void LLParcel::startSale(const LLUUID& buyer_id, BOOL is_buyer_group) { @@ -1117,6 +1237,12 @@ void LLParcel::clearParcel() mObscureMusic = 1; mMediaWidth = 0; mMediaHeight = 0; + setMediaCurrentURL(LLStringUtil::null); + setMediaURLFilterList(LLSD::emptyArray()); + setMediaURLFilterEnable(FALSE); + setMediaAllowNavigate(TRUE); + setMediaPreventCameraZoom(FALSE); + setMediaURLTimeout(0.0f); setMusicURL(LLStringUtil::null); setInEscrow(FALSE); setAuthorizedBuyerID(LLUUID::null); diff --git a/indra/llinventory/llparcel.h b/indra/llinventory/llparcel.h index 71baac8d89..aa8391230c 100644 --- a/indra/llinventory/llparcel.h +++ b/indra/llinventory/llparcel.h @@ -248,6 +248,14 @@ public: void setObscureMusic( U8 flagIn ) { mObscureMusic = flagIn; } void setMediaWidth(S32 width); void setMediaHeight(S32 height); + void setMediaCurrentURL(const std::string& url); + void setMediaURLFilterEnable(U8 enable) { mMediaURLFilterEnable = enable; } + void setMediaURLFilterList(LLSD list); + void setMediaAllowNavigate(U8 enable) { mMediaAllowNavigate = enable; } + void setMediaURLTimeout(F32 timeout) { mMediaURLTimeout = timeout; } + void setMediaPreventCameraZoom(U8 enable) { mMediaPreventCameraZoom = enable; } + + void setMediaURLResetTimer(F32 time); virtual void setLocalID(S32 local_id); // blow away all the extra crap lurking in parcels, including urls, access lists, etc @@ -300,7 +308,8 @@ public: // BOOL importStream(std::istream& input_stream); BOOL importAccessEntry(std::istream& input_stream, LLAccessEntry* entry); -// BOOL exportStream(std::ostream& output_stream); + BOOL importMediaURLFilter(std::istream& input_stream, std::string& url); + // BOOL exportStream(std::ostream& output_stream); void packMessage(LLMessageSystem* msg); void packMessage(LLSD& msg); @@ -342,8 +351,15 @@ public: S32 getMediaHeight() const { return mMediaHeight; } U8 getMediaAutoScale() const { return mMediaAutoScale; } U8 getMediaLoop() const { return mMediaLoop; } + const std::string& getMediaCurrentURL() const { return mMediaCurrentURL; } U8 getObscureMedia() const { return mObscureMedia; } U8 getObscureMusic() const { return mObscureMusic; } + U8 getMediaURLFilterEnable() const { return mMediaURLFilterEnable; } + LLSD getMediaURLFilterList() const { return mMediaURLFilterList; } + U8 getMediaAllowNavigate() const { return mMediaAllowNavigate; } + F32 getMediaURLTimeout() const { return mMediaURLTimeout; } + U8 getMediaPreventCameraZoom() const { return mMediaPreventCameraZoom; } + S32 getLocalID() const { return mLocalID; } const LLUUID& getOwnerID() const { return mOwnerID; } const LLUUID& getGroupID() const { return mGroupID; } @@ -414,6 +430,10 @@ public: void completeSale(U32& type, U8& flags, LLUUID& to_id); void clearSale(); + + BOOL isMediaResetTimerExpired(const U64& time); + + // more accessors U32 getParcelFlags() const { return mParcelFlags; } @@ -592,6 +612,8 @@ protected: LLVector3 mUserLookAt; ELandingType mLandingType; LLTimer mSaleTimerExpires; + LLTimer mMediaResetTimer; + S32 mGraceExtension; // This value is non-zero if there is an auction associated with @@ -619,9 +641,15 @@ protected: S32 mMediaHeight; U8 mMediaAutoScale; U8 mMediaLoop; + std::string mMediaCurrentURL; U8 mObscureMedia; U8 mObscureMusic; LLUUID mMediaID; + U8 mMediaURLFilterEnable; + LLSD mMediaURLFilterList; + U8 mMediaAllowNavigate; + U8 mMediaPreventCameraZoom; + F32 mMediaURLTimeout; S32 mPassPrice; F32 mPassHours; LLVector3 mAABBMin; @@ -651,6 +679,7 @@ public: std::map<LLUUID,LLAccessEntry> mBanList; std::map<LLUUID,LLAccessEntry> mTempBanList; std::map<LLUUID,LLAccessEntry> mTempAccessList; + }; diff --git a/indra/llmath/llrect.h b/indra/llmath/llrect.h index 894f0a7ed0..c03a331aff 100644 --- a/indra/llmath/llrect.h +++ b/indra/llmath/llrect.h @@ -141,10 +141,20 @@ public: // Note: Does NOT follow GL_QUAD conventions: the top and right edges ARE considered part of the rect // returns TRUE if any part of rect is is inside this LLRect - BOOL rectInRect(const LLRectBase* rect) const + BOOL overlaps(const LLRectBase& rect) const { - return mLeft <= rect->mRight && rect->mLeft <= mRight && - mBottom <= rect->mTop && rect->mBottom <= mTop ; + return !(mLeft > rect.mRight + || mRight < rect.mLeft + || mBottom > rect.mTop + || mTop < rect.mBottom); + } + + BOOL contains(const LLRectBase& rect) const + { + return mLeft <= rect.mLeft + && mRight >= rect.mRight + && mBottom <= rect.mBottom + && mTop >= rect.mTop; } LLRectBase& set(Type left, Type top, Type right, Type bottom) @@ -223,26 +233,25 @@ public: return mLeft <= mRight && mBottom <= mTop; } - bool isNull() const + bool isEmpty() const { return mLeft == mRight || mBottom == mTop; } - bool notNull() const + bool notEmpty() const { - return !isNull(); + return !isEmpty(); } - LLRectBase& unionWith(const LLRectBase &other) + void unionWith(const LLRectBase &other) { mLeft = llmin(mLeft, other.mLeft); mRight = llmax(mRight, other.mRight); mBottom = llmin(mBottom, other.mBottom); mTop = llmax(mTop, other.mTop); - return *this; } - LLRectBase& intersectWith(const LLRectBase &other) + void intersectWith(const LLRectBase &other) { mLeft = llmax(mLeft, other.mLeft); mRight = llmin(mRight, other.mRight); @@ -256,7 +265,6 @@ public: { mBottom = mTop; } - return *this; } friend std::ostream &operator<<(std::ostream &s, const LLRectBase &rect) diff --git a/indra/llmath/llvolume.cpp b/indra/llmath/llvolume.cpp index 1250f539f5..a0357a32cc 100644 --- a/indra/llmath/llvolume.cpp +++ b/indra/llmath/llvolume.cpp @@ -3370,7 +3370,8 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices, std::vector<S32> &segments, const LLVector3& obj_cam_vec, const LLMatrix4& mat, - const LLMatrix3& norm_mat) + const LLMatrix3& norm_mat, + S32 face_mask) { LLMemType m1(LLMemType::MTYPE_VOLUME); @@ -3378,12 +3379,17 @@ void LLVolume::generateSilhouetteVertices(std::vector<LLVector3> &vertices, normals.clear(); segments.clear(); + S32 cur_index = 0; //for each face for (face_list_t::iterator iter = mVolumeFaces.begin(); iter != mVolumeFaces.end(); ++iter) { const LLVolumeFace& face = *iter; + if (!(face_mask & (0x1 << cur_index++))) + { + continue; + } if (face.mTypeMask & (LLVolumeFace::CAP_MASK)) { } diff --git a/indra/llmath/llvolume.h b/indra/llmath/llvolume.h index af46da05d8..871b334452 100644 --- a/indra/llmath/llvolume.h +++ b/indra/llmath/llvolume.h @@ -902,9 +902,13 @@ public: // returns number of triangle indeces required for path/profile mesh S32 getNumTriangleIndices() const; - void generateSilhouetteVertices(std::vector<LLVector3> &vertices, std::vector<LLVector3> &normals, std::vector<S32> &segments, const LLVector3& view_vec, - const LLMatrix4& mat, - const LLMatrix3& norm_mat); + void generateSilhouetteVertices(std::vector<LLVector3> &vertices, + std::vector<LLVector3> &normals, + std::vector<S32> &segments, + const LLVector3& view_vec, + const LLMatrix4& mat, + const LLMatrix3& norm_mat, + S32 face_index); //get the face index of the face that intersects with the given line segment at the point //closest to start. Moves end to the point of intersection. Returns -1 if no intersection. diff --git a/indra/llmessage/llpumpio.cpp b/indra/llmessage/llpumpio.cpp index 3e3f0b37a7..5e9dfd81fa 100644 --- a/indra/llmessage/llpumpio.cpp +++ b/indra/llmessage/llpumpio.cpp @@ -444,11 +444,13 @@ void LLPumpIO::pump() pump(DEFAULT_POLL_TIMEOUT); } +static LLFastTimer::DeclareTimer FTM_PUMP("Pump"); + //timeout is in microseconds void LLPumpIO::pump(const S32& poll_timeout) { LLMemType m1(LLMemType::MTYPE_IO_PUMP); - LLFastTimer t1(LLFastTimer::FTM_PUMP); + LLFastTimer t1(FTM_PUMP); //llinfos << "LLPumpIO::pump()" << llendl; // Run any pending runners. diff --git a/indra/llmessage/lltemplatemessagereader.cpp b/indra/llmessage/lltemplatemessagereader.cpp index 80ea6ce96b..8c9eb7ed42 100644 --- a/indra/llmessage/lltemplatemessagereader.cpp +++ b/indra/llmessage/lltemplatemessagereader.cpp @@ -531,6 +531,8 @@ void LLTemplateMessageReader::logRanOffEndOfPacket( const LLHost& host, const S3 gMessageSystem->callExceptionFunc(MX_RAN_OFF_END_OF_PACKET); } +static LLFastTimer::DeclareTimer FTM_PROCESS_MESSAGES("Process Messages"); + // decode a given message BOOL LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender ) { @@ -714,7 +716,7 @@ BOOL LLTemplateMessageReader::decodeData(const U8* buffer, const LLHost& sender } { - LLFastTimer t(LLFastTimer::FTM_PROCESS_MESSAGES); + LLFastTimer t(FTM_PROCESS_MESSAGES); if( !mCurrentRMessageTemplate->callHandlerFunc(gMessageSystem) ) { llwarns << "Message from " << sender << " with no handler function received: " << mCurrentRMessageTemplate->mName << llendl; diff --git a/indra/llplugin/CMakeLists.txt b/indra/llplugin/CMakeLists.txt new file mode 100644 index 0000000000..6706775d4f --- /dev/null +++ b/indra/llplugin/CMakeLists.txt @@ -0,0 +1,55 @@ +# -*- cmake -*- + +project(llplugin) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLMath) +include(LLMessage) +include(LLRender) +include(LLXML) +include(LLWindow) + +include_directories( + ${LLCOMMON_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLMESSAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLXML_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} + ) + +set(llplugin_SOURCE_FILES + llpluginclassmedia.cpp + llplugininstance.cpp + llpluginmessage.cpp + llpluginmessagepipe.cpp + llpluginprocesschild.cpp + llpluginprocessparent.cpp + llpluginsharedmemory.cpp + ) + +set(llplugin_HEADER_FILES + CMakeLists.txt + + llpluginclassmedia.h + llpluginclassmediaowner.h + llplugininstance.h + llpluginmessage.h + llpluginmessageclasses.h + llpluginmessagepipe.h + llpluginprocesschild.h + llpluginprocessparent.h + llpluginsharedmemory.h + ) + +set_source_files_properties(${llplugin_HEADER_FILES} + PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND llplugin_SOURCE_FILES ${llplugin_HEADER_FILES}) + +add_library (llplugin ${llplugin_SOURCE_FILES}) + +add_subdirectory(slplugin) diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp new file mode 100644 index 0000000000..54f153d182 --- /dev/null +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -0,0 +1,1042 @@ +/** + * @file llpluginclassmedia.cpp + * @brief LLPluginClassMedia handles a plugin which knows about the "media" message class. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "indra_constants.h" + +#include "llpluginclassmedia.h" +#include "llpluginmessageclasses.h" + +static int LOW_PRIORITY_TEXTURE_SIZE_DEFAULT = 256; + +static int nextPowerOf2( int value ) +{ + int next_power_of_2 = 1; + while ( next_power_of_2 < value ) + { + next_power_of_2 <<= 1; + } + + return next_power_of_2; +} + +LLPluginClassMedia::LLPluginClassMedia(LLPluginClassMediaOwner *owner) +{ + mOwner = owner; + mPlugin = NULL; + reset(); +} + + +LLPluginClassMedia::~LLPluginClassMedia() +{ + reset(); +} + +bool LLPluginClassMedia::init(const std::string &launcher_filename, const std::string &plugin_filename) +{ + LL_DEBUGS("Plugin") << "launcher: " << launcher_filename << LL_ENDL; + LL_DEBUGS("Plugin") << "plugin: " << plugin_filename << LL_ENDL; + + mPlugin = new LLPluginProcessParent(this); + mPlugin->setSleepTime(mSleepTime); + mPlugin->init(launcher_filename, plugin_filename); + + return true; +} + + +void LLPluginClassMedia::reset() +{ + if(mPlugin != NULL) + { + delete mPlugin; + mPlugin = NULL; + } + + mTextureParamsReceived = false; + mRequestedTextureDepth = 0; + mRequestedTextureInternalFormat = 0; + mRequestedTextureFormat = 0; + mRequestedTextureType = 0; + mRequestedTextureSwapBytes = false; + mRequestedTextureCoordsOpenGL = false; + mTextureSharedMemorySize = 0; + mTextureSharedMemoryName.clear(); + mDefaultMediaWidth = 0; + mDefaultMediaHeight = 0; + mNaturalMediaWidth = 0; + mNaturalMediaHeight = 0; + mSetMediaWidth = -1; + mSetMediaHeight = -1; + mRequestedMediaWidth = 0; + mRequestedMediaHeight = 0; + mTextureWidth = 0; + mTextureHeight = 0; + mMediaWidth = 0; + mMediaHeight = 0; + mDirtyRect = LLRect::null; + mAutoScaleMedia = false; + mRequestedVolume = 1.0f; + mPriority = PRIORITY_NORMAL; + mLowPrioritySizeLimit = LOW_PRIORITY_TEXTURE_SIZE_DEFAULT; + mAllowDownsample = false; + mPadding = 0; + mStatus = LLPluginClassMediaOwner::MEDIA_NONE; + mSleepTime = 1.0f / 100.0f; + mCanCut = false; + mCanCopy = false; + mCanPaste = false; + mMediaName.clear(); + mMediaDescription.clear(); + + // media_browser class + mNavigateURI.clear(); + mNavigateResultCode = -1; + mNavigateResultString.clear(); + mHistoryBackAvailable = false; + mHistoryForwardAvailable = false; + mStatusText.clear(); + mProgressPercent = 0; + + // media_time class + mCurrentTime = 0.0f; + mDuration = 0.0f; + mCurrentRate = 0.0f; +} + +void LLPluginClassMedia::idle(void) +{ + if(mPlugin) + { + mPlugin->idle(); + } + + if((mMediaWidth == -1) || (!mTextureParamsReceived) || (mPlugin == NULL)) + { + // Can't process a size change at this time + } + else if((mRequestedMediaWidth != mMediaWidth) || (mRequestedMediaHeight != mMediaHeight)) + { + // Calculate the correct size for the media texture + mRequestedTextureHeight = mRequestedMediaHeight; + if(mPadding < 0) + { + // negative values indicate the plugin wants a power of 2 + mRequestedTextureWidth = nextPowerOf2(mRequestedMediaWidth); + } + else + { + mRequestedTextureWidth = mRequestedMediaWidth; + + if(mPadding > 1) + { + // Pad up to a multiple of the specified number of bytes per row + int rowbytes = mRequestedTextureWidth * mRequestedTextureDepth; + int pad = rowbytes % mPadding; + if(pad != 0) + { + rowbytes += mPadding - pad; + } + + if(rowbytes % mRequestedTextureDepth == 0) + { + mRequestedTextureWidth = rowbytes / mRequestedTextureDepth; + } + else + { + LL_WARNS("Plugin") << "Unable to pad texture width, padding size " << mPadding << "is not a multiple of pixel size " << mRequestedTextureDepth << LL_ENDL; + } + } + } + + + // Size change has been requested but not initiated yet. + size_t newsize = mRequestedTextureWidth * mRequestedTextureHeight * mRequestedTextureDepth; + + // Add an extra line for padding, just in case. + newsize += mRequestedTextureWidth * mRequestedTextureDepth; + + if(newsize != mTextureSharedMemorySize) + { + if(!mTextureSharedMemoryName.empty()) + { + // Tell the plugin to remove the old memory segment + mPlugin->removeSharedMemory(mTextureSharedMemoryName); + mTextureSharedMemoryName.clear(); + } + + mTextureSharedMemorySize = newsize; + mTextureSharedMemoryName = mPlugin->addSharedMemory(mTextureSharedMemorySize); + if(!mTextureSharedMemoryName.empty()) + { + void *addr = mPlugin->getSharedMemoryAddress(mTextureSharedMemoryName); + + // clear texture memory to avoid random screen visual fuzz from uninitialized texture data + memset( addr, 0x00, newsize ); + + // We could do this to force an update, but textureValid() will still be returning false until the first roundtrip to the plugin, + // so it may not be worthwhile. + // mDirtyRect.setOriginAndSize(0, 0, mRequestedMediaWidth, mRequestedMediaHeight); + } + } + + // This is our local indicator that a change is in progress. + mTextureWidth = -1; + mTextureHeight = -1; + mMediaWidth = -1; + mMediaHeight = -1; + + // This invalidates any existing dirty rect. + resetDirty(); + + // Send a size change message to the plugin + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change"); + message.setValue("name", mTextureSharedMemoryName); + message.setValueS32("width", mRequestedMediaWidth); + message.setValueS32("height", mRequestedMediaHeight); + message.setValueS32("texture_width", mRequestedTextureWidth); + message.setValueS32("texture_height", mRequestedTextureHeight); + mPlugin->sendMessage(message); // DO NOT just use sendMessage() here -- we want this to jump ahead of the queue. + + LL_DEBUGS("Plugin") << "Sending size_change" << LL_ENDL; + } + } + + if(mPlugin && mPlugin->isRunning()) + { + // Send queued messages + while(!mSendQueue.empty()) + { + LLPluginMessage message = mSendQueue.front(); + mSendQueue.pop(); + mPlugin->sendMessage(message); + } + } +} + +int LLPluginClassMedia::getTextureWidth() const +{ + return nextPowerOf2(mTextureWidth); +} + +int LLPluginClassMedia::getTextureHeight() const +{ + return nextPowerOf2(mTextureHeight); +} + +unsigned char* LLPluginClassMedia::getBitsData() +{ + unsigned char *result = NULL; + if((mPlugin != NULL) && !mTextureSharedMemoryName.empty()) + { + result = (unsigned char*)mPlugin->getSharedMemoryAddress(mTextureSharedMemoryName); + } + return result; +} + +void LLPluginClassMedia::setSize(int width, int height) +{ + mSetMediaWidth = width; + mSetMediaHeight = height; + + setSizeInternal(); +} + +void LLPluginClassMedia::setSizeInternal(void) +{ + if((mSetMediaWidth > 0) && (mSetMediaHeight > 0)) + { + mRequestedMediaWidth = mSetMediaWidth; + mRequestedMediaHeight = mSetMediaHeight; + } + else + { + mRequestedMediaWidth = mDefaultMediaWidth; + mRequestedMediaHeight = mDefaultMediaHeight; + } + + if(mAllowDownsample) + { + switch(mPriority) + { + case PRIORITY_LOW: + // Reduce maximum texture dimension to (or below) mLowPrioritySizeLimit + while((mRequestedMediaWidth > mLowPrioritySizeLimit) || (mRequestedMediaHeight > mLowPrioritySizeLimit)) + { + mRequestedMediaWidth /= 2; + mRequestedMediaHeight /= 2; + } + break; + + default: + // Don't adjust texture size + break; + } + } + + if(mAutoScaleMedia) + { + mRequestedMediaWidth = nextPowerOf2(mRequestedMediaWidth); + mRequestedMediaHeight = nextPowerOf2(mRequestedMediaHeight); + } +} + +void LLPluginClassMedia::setAutoScale(bool auto_scale) +{ + if(auto_scale != mAutoScaleMedia) + { + mAutoScaleMedia = auto_scale; + setSizeInternal(); + } +} + +bool LLPluginClassMedia::textureValid(void) +{ + if( + !mTextureParamsReceived || + mTextureWidth <= 0 || + mTextureHeight <= 0 || + mMediaWidth <= 0 || + mMediaHeight <= 0 || + mRequestedMediaWidth != mMediaWidth || + mRequestedMediaHeight != mMediaHeight || + getBitsData() == NULL + ) + return false; + + return true; +} + +bool LLPluginClassMedia::getDirty(LLRect *dirty_rect) +{ + bool result = !mDirtyRect.isEmpty(); + + if(dirty_rect != NULL) + { + *dirty_rect = mDirtyRect; + } + + return result; +} + +void LLPluginClassMedia::resetDirty(void) +{ + mDirtyRect = LLRect::null; +} + +std::string LLPluginClassMedia::translateModifiers(MASK modifiers) +{ + std::string result; + + + if(modifiers & MASK_CONTROL) + { + result += "control|"; + } + + if(modifiers & MASK_ALT) + { + result += "alt|"; + } + + if(modifiers & MASK_SHIFT) + { + result += "shift|"; + } + + // TODO: should I deal with platform differences here or in callers? + // TODO: how do we deal with the Mac "command" key? +/* + if(modifiers & MASK_SOMETHING) + { + result += "meta|"; + } +*/ + return result; +} + +void LLPluginClassMedia::mouseEvent(EMouseEventType type, int x, int y, MASK modifiers) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "mouse_event"); + std::string temp; + switch(type) + { + case MOUSE_EVENT_DOWN: temp = "down"; break; + case MOUSE_EVENT_UP: temp = "up"; break; + case MOUSE_EVENT_MOVE: temp = "move"; break; + case MOUSE_EVENT_DOUBLE_CLICK: temp = "double_click"; break; + } + message.setValue("event", temp); + + message.setValueS32("x", x); + + // Incoming coordinates are OpenGL-style ((0,0) = lower left), so flip them here if the plugin has requested it. + if(!mRequestedTextureCoordsOpenGL) + { + // TODO: Should I use mMediaHeight or mRequestedMediaHeight here? + y = mMediaHeight - y; + } + message.setValueS32("y", y); + + message.setValue("modifiers", translateModifiers(modifiers)); + + sendMessage(message); +} + +bool LLPluginClassMedia::keyEvent(EKeyEventType type, int key_code, MASK modifiers) +{ + bool result = true; + + // FIXME: + // HACK: we don't have an easy way to tell if the plugin is going to handle a particular keycode. + // For now, return false for the ones the webkit plugin won't handle properly. + + switch(key_code) + { + case KEY_BACKSPACE: + case KEY_TAB: + case KEY_RETURN: + case KEY_PAD_RETURN: + case KEY_SHIFT: + case KEY_CONTROL: + case KEY_ALT: + case KEY_CAPSLOCK: + case KEY_ESCAPE: + case KEY_PAGE_UP: + case KEY_PAGE_DOWN: + case KEY_END: + case KEY_HOME: + case KEY_LEFT: + case KEY_UP: + case KEY_RIGHT: + case KEY_DOWN: + case KEY_INSERT: + case KEY_DELETE: + // These will be handled + break; + + default: + // regular ASCII characters will also be handled + if(key_code >= KEY_SPECIAL) + { + // Other "special" codes will not work properly. + result = false; + } + break; + } + + if(result) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "key_event"); + std::string temp; + switch(type) + { + case KEY_EVENT_DOWN: temp = "down"; break; + case KEY_EVENT_UP: temp = "up"; break; + case KEY_EVENT_REPEAT: temp = "repeat"; break; + } + message.setValue("event", temp); + + message.setValueS32("key", key_code); + + message.setValue("modifiers", translateModifiers(modifiers)); + + sendMessage(message); + } + + return result; +} + +void LLPluginClassMedia::scrollEvent(int x, int y, MASK modifiers) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "scroll_event"); + + message.setValueS32("x", x); + message.setValueS32("y", y); + message.setValue("modifiers", translateModifiers(modifiers)); + + sendMessage(message); +} + +bool LLPluginClassMedia::textInput(const std::string &text) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "text_event"); + + message.setValue("text", text); + + sendMessage(message); + + return true; +} + +void LLPluginClassMedia::loadURI(const std::string &uri) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "load_uri"); + + message.setValue("uri", uri); + + sendMessage(message); +} + +void LLPluginClassMedia::setPriority(EPriority priority) +{ + if(mPriority != priority) + { + mPriority = priority; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "set_priority"); + + std::string priority_string; + switch(priority) + { + case PRIORITY_STOPPED: + priority_string = "stopped"; + mSleepTime = 1.0f; + break; + case PRIORITY_HIDDEN: + priority_string = "hidden"; + mSleepTime = 1.0f; + break; + case PRIORITY_LOW: + priority_string = "low"; + mSleepTime = 1.0f / 50.0f; + break; + case PRIORITY_NORMAL: + priority_string = "normal"; + mSleepTime = 1.0f / 100.0f; + break; + case PRIORITY_HIGH: + priority_string = "high"; + mSleepTime = 1.0f / 100.0f; + break; + } + + message.setValue("priority", priority_string); + + sendMessage(message); + + if(mPlugin) + { + mPlugin->setSleepTime(mSleepTime); + } + + // This may affect the calculated size, so recalculate it here. + setSizeInternal(); + } +} + +void LLPluginClassMedia::setLowPrioritySizeLimit(int size) +{ + if(mLowPrioritySizeLimit != size) + { + mLowPrioritySizeLimit = size; + + // This may affect the calculated size, so recalculate it here. + setSizeInternal(); + } +} + + +void LLPluginClassMedia::cut() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_cut"); + sendMessage(message); +} + +void LLPluginClassMedia::copy() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_copy"); + sendMessage(message); +} + +void LLPluginClassMedia::paste() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_paste"); + sendMessage(message); +} + +/* virtual */ +void LLPluginClassMedia::receivePluginMessage(const LLPluginMessage &message) +{ + std::string message_class = message.getClass(); + + if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + std::string message_name = message.getName(); + if(message_name == "texture_params") + { + mRequestedTextureDepth = message.getValueS32("depth"); + mRequestedTextureInternalFormat = message.getValueU32("internalformat"); + mRequestedTextureFormat = message.getValueU32("format"); + mRequestedTextureType = message.getValueU32("type"); + mRequestedTextureSwapBytes = message.getValueBoolean("swap_bytes"); + mRequestedTextureCoordsOpenGL = message.getValueBoolean("coords_opengl"); + + // These two are optional, and will default to 0 if they're not specified. + mDefaultMediaWidth = message.getValueS32("default_width"); + mDefaultMediaHeight = message.getValueS32("default_height"); + + mAllowDownsample = message.getValueBoolean("allow_downsample"); + mPadding = message.getValueS32("padding"); + + setSizeInternal(); + + mTextureParamsReceived = true; + } + else if(message_name == "updated") + { + if(message.hasValue("left")) + { + LLRect newDirtyRect; + newDirtyRect.mLeft = message.getValueS32("left"); + newDirtyRect.mTop = message.getValueS32("top"); + newDirtyRect.mRight = message.getValueS32("right"); + newDirtyRect.mBottom = message.getValueS32("bottom"); + + // The plugin is likely to have top and bottom switched, due to vertical flip and OpenGL coordinate confusion. + // If they're backwards, swap them. + if(newDirtyRect.mTop < newDirtyRect.mBottom) + { + S32 temp = newDirtyRect.mTop; + newDirtyRect.mTop = newDirtyRect.mBottom; + newDirtyRect.mBottom = temp; + } + + if(mDirtyRect.isEmpty()) + { + mDirtyRect = newDirtyRect; + } + else + { + mDirtyRect.unionWith(newDirtyRect); + } + + LL_DEBUGS("Plugin") << "adjusted incoming rect is: (" + << newDirtyRect.mLeft << ", " + << newDirtyRect.mTop << ", " + << newDirtyRect.mRight << ", " + << newDirtyRect.mBottom << "), new dirty rect is: (" + << mDirtyRect.mLeft << ", " + << mDirtyRect.mTop << ", " + << mDirtyRect.mRight << ", " + << mDirtyRect.mBottom << ")" + << LL_ENDL; + + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CONTENT_UPDATED); + } + + + bool time_duration_updated = false; + + if(message.hasValue("current_time")) + { + mCurrentTime = message.getValueReal("current_time"); + time_duration_updated = true; + } + if(message.hasValue("duration")) + { + mDuration = message.getValueReal("duration"); + time_duration_updated = true; + } + + if(message.hasValue("current_rate")) + { + mCurrentRate = message.getValueReal("current_rate"); + } + + if(time_duration_updated) + { + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_TIME_DURATION_UPDATED); + } + + } + else if(message_name == "media_status") + { + std::string status = message.getValue("status"); + + LL_DEBUGS("Plugin") << "Status changed to: " << status << LL_ENDL; + + if(status == "loading") + { + mStatus = LLPluginClassMediaOwner::MEDIA_LOADING; + } + else if(status == "loaded") + { + mStatus = LLPluginClassMediaOwner::MEDIA_LOADED; + } + else if(status == "error") + { + mStatus = LLPluginClassMediaOwner::MEDIA_ERROR; + } + else if(status == "playing") + { + mStatus = LLPluginClassMediaOwner::MEDIA_PLAYING; + } + else if(status == "paused") + { + mStatus = LLPluginClassMediaOwner::MEDIA_PAUSED; + } + else + { + // empty string or any unknown string + mStatus = LLPluginClassMediaOwner::MEDIA_NONE; + } + } + else if(message_name == "size_change_request") + { + S32 width = message.getValueS32("width"); + S32 height = message.getValueS32("height"); + std::string name = message.getValue("name"); + + // TODO: check that name matches? + mNaturalMediaWidth = width; + mNaturalMediaHeight = height; + + setSize(width, height); + } + else if(message_name == "size_change_response") + { + std::string name = message.getValue("name"); + + // TODO: check that name matches? + + mTextureWidth = message.getValueS32("texture_width"); + mTextureHeight = message.getValueS32("texture_height"); + mMediaWidth = message.getValueS32("width"); + mMediaHeight = message.getValueS32("height"); + + // This invalidates any existing dirty rect. + resetDirty(); + + // TODO: should we verify that the plugin sent back the right values? + // Two size changes in a row may cause them to not match, due to queueing, etc. + + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_SIZE_CHANGED); + } + else if(message_name == "cursor_changed") + { + mCursorName = message.getValue("name"); + + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CURSOR_CHANGED); + } + else if(message_name == "edit_state") + { + if(message.hasValue("cut")) + { + mCanCut = message.getValueBoolean("cut"); + } + if(message.hasValue("copy")) + { + mCanCopy = message.getValueBoolean("copy"); + } + if(message.hasValue("paste")) + { + mCanPaste = message.getValueBoolean("paste"); + } + } + else + { + LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) + { + std::string message_name = message.getName(); + if(message_name == "navigate_begin") + { + mNavigateURI = message.getValue("uri"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_NAVIGATE_BEGIN); + } + else if(message_name == "navigate_complete") + { + mNavigateURI = message.getValue("uri"); + mNavigateResultCode = message.getValueS32("result_code"); + mNavigateResultString = message.getValue("result_string"); + mHistoryBackAvailable = message.getValueBoolean("history_back_available"); + mHistoryForwardAvailable = message.getValueBoolean("history_forward_available"); + + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_NAVIGATE_COMPLETE); + } + else if(message_name == "progress") + { + mProgressPercent = message.getValueS32("percent"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PROGRESS_UPDATED); + } + else if(message_name == "status_text") + { + mStatusText = message.getValue("status"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_STATUS_TEXT_CHANGED); + } + else if(message_name == "location_changed") + { + mLocation = message.getValue("uri"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_LOCATION_CHANGED); + } + else if(message_name == "click_href") + { + mClickURL = message.getValue("uri"); + mClickTarget = message.getValue("target"); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_HREF); + } + else if(message_name == "click_nofollow") + { + mClickURL = message.getValue("uri"); + mClickTarget.clear(); + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_CLICK_LINK_NOFOLLOW); + } + else + { + LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + std::string message_name = message.getName(); + + // This class hasn't defined any incoming messages yet. +// if(message_name == "message_name") +// { +// } +// else + { + LL_WARNS("Plugin") << "Unknown " << message_name << " class message: " << message_name << LL_ENDL; + } + } + +} + +/* virtual */ +void LLPluginClassMedia::pluginDied() +{ + mediaEvent(LLPluginClassMediaOwner::MEDIA_EVENT_PLUGIN_FAILED); +} + +void LLPluginClassMedia::mediaEvent(LLPluginClassMediaOwner::EMediaEvent event) +{ + if(mOwner) + { + mOwner->handleMediaEvent(this, event); + } +} + +void LLPluginClassMedia::sendMessage(const LLPluginMessage &message) +{ + if(mPlugin && mPlugin->isRunning()) + { + mPlugin->sendMessage(message); + } + else + { + // The plugin isn't set up yet -- queue this message to be sent after initialization. + mSendQueue.push(message); + } +} + +//////////////////////////////////////////////////////////// +// MARK: media_browser class functions +bool LLPluginClassMedia::pluginSupportsMediaBrowser(void) +{ + std::string version = mPlugin->getMessageClassVersion(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER); + return !version.empty(); +} + +void LLPluginClassMedia::focus(bool focused) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "focus"); + + message.setValueBoolean("focused", focused); + + sendMessage(message); +} + +void LLPluginClassMedia::clear_cache() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "clear_cache"); + sendMessage(message); +} + +void LLPluginClassMedia::clear_cookies() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "clear_cookies"); + sendMessage(message); +} + +void LLPluginClassMedia::enable_cookies(bool enable) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "enable_cookies"); + sendMessage(message); +} + +void LLPluginClassMedia::proxy_setup(bool enable, const std::string &host, int port) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "proxy_setup"); + + message.setValueBoolean("enable", enable); + message.setValue("host", host); + message.setValueS32("port", port); + + sendMessage(message); +} + +void LLPluginClassMedia::browse_stop() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "browse_stop"); + sendMessage(message); +} + +void LLPluginClassMedia::browse_reload(bool ignore_cache) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "browse_reload"); + + message.setValueBoolean("ignore_cache", ignore_cache); + + sendMessage(message); +} + +void LLPluginClassMedia::browse_forward() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "browse_forward"); + sendMessage(message); +} + +void LLPluginClassMedia::browse_back() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "browse_back"); + sendMessage(message); +} + +void LLPluginClassMedia::set_status_redirect(int code, const std::string &url) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_status_redirect"); + + message.setValueS32("code", code); + message.setValue("url", url); + + sendMessage(message); +} + +void LLPluginClassMedia::setBrowserUserAgent(const std::string& user_agent) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "set_user_agent"); + + message.setValue("user_agent", user_agent); + + sendMessage(message); +} + +void LLPluginClassMedia::crashPlugin() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "crash"); + + sendMessage(message); +} + +void LLPluginClassMedia::hangPlugin() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hang"); + + sendMessage(message); +} + + +//////////////////////////////////////////////////////////// +// MARK: media_time class functions +bool LLPluginClassMedia::pluginSupportsMediaTime(void) +{ + std::string version = mPlugin->getMessageClassVersion(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME); + return !version.empty(); +} + +void LLPluginClassMedia::stop() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "stop"); + sendMessage(message); +} + +void LLPluginClassMedia::start(float rate) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "start"); + + message.setValueReal("rate", rate); + + sendMessage(message); +} + +void LLPluginClassMedia::pause() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "pause"); + sendMessage(message); +} + +void LLPluginClassMedia::seek(float time) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "seek"); + + message.setValueReal("time", time); + + sendMessage(message); +} + +void LLPluginClassMedia::setLoop(bool loop) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "set_loop"); + + message.setValueBoolean("loop", loop); + + sendMessage(message); +} + +void LLPluginClassMedia::setVolume(float volume) +{ + if(volume != mRequestedVolume) + { + mRequestedVolume = volume; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME, "set_volume"); + + message.setValueReal("volume", volume); + + sendMessage(message); + } +} + +void LLPluginClassMedia::initializeUrlHistory(const LLSD& url_history) +{ + // Send URL history to plugin + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "init_history"); + message.setValueLLSD("history", url_history); + sendMessage(message); + + LL_DEBUGS("Plugin") << "Sending history" << LL_ENDL; +} + diff --git a/indra/llplugin/llpluginclassmedia.h b/indra/llplugin/llpluginclassmedia.h new file mode 100644 index 0000000000..7a8586fe2f --- /dev/null +++ b/indra/llplugin/llpluginclassmedia.h @@ -0,0 +1,333 @@ +/** + * @file llpluginclassmedia.h + * @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINCLASSMEDIA_H +#define LL_LLPLUGINCLASSMEDIA_H + +#include "llgl.h" +#include "llpluginprocessparent.h" +#include "llrect.h" +#include "llpluginclassmediaowner.h" +#include <queue> + + +class LLPluginClassMedia : public LLPluginProcessParentOwner +{ + LOG_CLASS(LLPluginClassMedia); +public: + LLPluginClassMedia(LLPluginClassMediaOwner *owner); + virtual ~LLPluginClassMedia(); + + // local initialization, called by the media manager when creating a source + virtual bool init(const std::string &launcher_filename, const std::string &plugin_filename); + + // undoes everything init() didm called by the media manager when destroying a source + virtual void reset(); + + void idle(void); + + // All of these may return 0 or an actual valid value. + // Callers need to check the return for 0, and not use the values in that case. + int getWidth() const { return (mMediaWidth > 0) ? mMediaWidth : 0; }; + int getHeight() const { return (mMediaHeight > 0) ? mMediaHeight : 0; }; + int getNaturalWidth() const { return mNaturalMediaWidth; }; + int getNaturalHeight() const { return mNaturalMediaHeight; }; + int getSetWidth() const { return mSetMediaWidth; }; + int getSetHeight() const { return mSetMediaHeight; }; + int getBitsWidth() const { return (mTextureWidth > 0) ? mTextureWidth : 0; }; + int getBitsHeight() const { return (mTextureHeight > 0) ? mTextureHeight : 0; }; + int getTextureWidth() const; + int getTextureHeight() const; + + // This may return NULL. Callers need to check for and handle this case. + unsigned char* getBitsData(); + + // gets the format details of the texture data + // These may return 0 if they haven't been set up yet. The caller needs to detect this case. + int getTextureDepth() const { return mRequestedTextureDepth; }; + int getTextureFormatInternal() const { return mRequestedTextureInternalFormat; }; + int getTextureFormatPrimary() const { return mRequestedTextureFormat; }; + int getTextureFormatType() const { return mRequestedTextureType; }; + bool getTextureFormatSwapBytes() const { return mRequestedTextureSwapBytes; }; + bool getTextureCoordsOpenGL() const { return mRequestedTextureCoordsOpenGL; }; + + void setSize(int width, int height); + void setAutoScale(bool auto_scale); + + // Returns true if all of the texture parameters (depth, format, size, and texture size) are set up and consistent. + // This will initially be false, and will also be false for some time after setSize while the resize is processed. + // Note that if this returns true, it is safe to use all the get() functions above without checking for invalid return values + // until you call idle() again. + bool textureValid(void); + + bool getDirty(LLRect *dirty_rect = NULL); + void resetDirty(void); + + typedef enum + { + MOUSE_EVENT_DOWN, + MOUSE_EVENT_UP, + MOUSE_EVENT_MOVE, + MOUSE_EVENT_DOUBLE_CLICK + }EMouseEventType; + + void mouseEvent(EMouseEventType type, int x, int y, MASK modifiers); + + typedef enum + { + KEY_EVENT_DOWN, + KEY_EVENT_UP, + KEY_EVENT_REPEAT + }EKeyEventType; + + bool keyEvent(EKeyEventType type, int key_code, MASK modifiers); + + void scrollEvent(int x, int y, MASK modifiers); + + // Text may be unicode (utf8 encoded) + bool textInput(const std::string &text); + + void loadURI(const std::string &uri); + + // "Loading" means uninitialized or any state prior to fully running (processing commands) + bool isPluginLoading(void) { return mPlugin?mPlugin->isLoading():false; }; + + // "Running" means the steady state -- i.e. processing messages + bool isPluginRunning(void) { return mPlugin?mPlugin->isRunning():false; }; + + // "Exited" means any regular or error state after "Running" (plugin may have crashed or exited normally) + bool isPluginExited(void) { return mPlugin?mPlugin->isDone():false; }; + + std::string getPluginVersion() { return mPlugin?mPlugin->getPluginVersion():std::string(""); }; + + bool getDisableTimeout() { return mPlugin?mPlugin->getDisableTimeout():false; }; + void setDisableTimeout(bool disable) { if(mPlugin) mPlugin->setDisableTimeout(disable); }; + + // Inherited from LLPluginProcessParentOwner + /* virtual */ void receivePluginMessage(const LLPluginMessage &message); + /* virtual */ void pluginDied(); + + + typedef enum + { + PRIORITY_STOPPED, // media is not playing, shouldn't need to update at all. + PRIORITY_HIDDEN, // media is not being displayed or is out of view, don't need to do graphic updates, but may still update audio, playhead, etc. + PRIORITY_LOW, // media is in the far distance, may be rendered at reduced size + PRIORITY_NORMAL, // normal (default) priority + PRIORITY_HIGH // media has user focus and/or is taking up most of the screen + }EPriority; + + void setPriority(EPriority priority); + void setLowPrioritySizeLimit(int size); + + // Valid after a MEDIA_EVENT_CURSOR_CHANGED event + std::string getCursorName() const { return mCursorName; }; + + LLPluginClassMediaOwner::EMediaStatus getStatus() const { return mStatus; } + + void cut(); + bool canCut() const { return mCanCut; }; + + void copy(); + bool canCopy() const { return mCanCopy; }; + + void paste(); + bool canPaste() const { return mCanPaste; }; + + /////////////////////////////////// + // media browser class functions + bool pluginSupportsMediaBrowser(void); + + void focus(bool focused); + void clear_cache(); + void clear_cookies(); + void enable_cookies(bool enable); + void proxy_setup(bool enable, const std::string &host = LLStringUtil::null, int port = 0); + void browse_stop(); + void browse_reload(bool ignore_cache = false); + void browse_forward(); + void browse_back(); + void set_status_redirect(int code, const std::string &url); + void setBrowserUserAgent(const std::string& user_agent); + + // This is valid after MEDIA_EVENT_NAVIGATE_BEGIN or MEDIA_EVENT_NAVIGATE_COMPLETE + std::string getNavigateURI() const { return mNavigateURI; }; + + // These are valid after MEDIA_EVENT_NAVIGATE_COMPLETE + S32 getNavigateResultCode() const { return mNavigateResultCode; }; + std::string getNavigateResultString() const { return mNavigateResultString; }; + bool getHistoryBackAvailable() const { return mHistoryBackAvailable; }; + bool getHistoryForwardAvailable() const { return mHistoryForwardAvailable; }; + + // This is valid after MEDIA_EVENT_PROGRESS_UPDATED + int getProgressPercent() const { return mProgressPercent; }; + + // This is valid after MEDIA_EVENT_STATUS_TEXT_CHANGED + std::string getStatusText() const { return mStatusText; }; + + // This is valid after MEDIA_EVENT_LOCATION_CHANGED + std::string getLocation() const { return mLocation; }; + + // This is valid after MEDIA_EVENT_CLICK_LINK_HREF or MEDIA_EVENT_CLICK_LINK_NOFOLLOW + std::string getClickURL() const { return mClickURL; }; + + // This is valid after MEDIA_EVENT_CLICK_LINK_HREF + std::string getClickTarget() const { return mClickTarget; }; + + std::string getMediaName() const { return mMediaName; }; + std::string getMediaDescription() const { return mMediaDescription; }; + + // Crash the plugin. If you use this outside of a testbed, you will be punished. + void crashPlugin(); + + // Hang the plugin. If you use this outside of a testbed, you will be punished. + void hangPlugin(); + + /////////////////////////////////// + // media time class functions + bool pluginSupportsMediaTime(void); + void stop(); + void start(float rate = 0.0f); + void pause(); + void seek(float time); + void setLoop(bool loop); + void setVolume(float volume); + + F64 getCurrentTime(void) const { return mCurrentTime; }; + F64 getDuration(void) const { return mDuration; }; + F64 getCurrentPlayRate(void) { return mCurrentRate; }; + + // Initialize the URL history of the plugin by sending + // "init_history" message + void initializeUrlHistory(const LLSD& url_history); + +protected: + LLPluginClassMediaOwner *mOwner; + + // Notify this object's owner that an event has occurred. + void mediaEvent(LLPluginClassMediaOwner::EMediaEvent event); + + void sendMessage(const LLPluginMessage &message); // Send message internally, either queueing or sending directly. + std::queue<LLPluginMessage> mSendQueue; // Used to queue messages while the plugin initializes. + + void setSizeInternal(void); + + bool mTextureParamsReceived; // the mRequestedTexture* fields are only valid when this is true + S32 mRequestedTextureDepth; + LLGLenum mRequestedTextureInternalFormat; + LLGLenum mRequestedTextureFormat; + LLGLenum mRequestedTextureType; + bool mRequestedTextureSwapBytes; + bool mRequestedTextureCoordsOpenGL; + + std::string mTextureSharedMemoryName; + size_t mTextureSharedMemorySize; + + // True to scale requested media up to the full size of the texture (i.e. next power of two) + bool mAutoScaleMedia; + + // default media size for the plugin, from the texture_params message. + int mDefaultMediaWidth; + int mDefaultMediaHeight; + + // Size that has been requested by the plugin itself + int mNaturalMediaWidth; + int mNaturalMediaHeight; + + // Size that has been requested with setSize() + int mSetMediaWidth; + int mSetMediaHeight; + + // Actual media size being set (may be affected by auto-scale) + int mRequestedMediaWidth; + int mRequestedMediaHeight; + + // Texture size calculated from actual media size + int mRequestedTextureWidth; + int mRequestedTextureHeight; + + // Size that the plugin has acknowledged + int mTextureWidth; + int mTextureHeight; + int mMediaWidth; + int mMediaHeight; + + float mRequestedVolume; + + // Priority of this media stream + EPriority mPriority; + int mLowPrioritySizeLimit; + + bool mAllowDownsample; + int mPadding; + + + LLPluginProcessParent *mPlugin; + + LLRect mDirtyRect; + + std::string translateModifiers(MASK modifiers); + + std::string mCursorName; + + LLPluginClassMediaOwner::EMediaStatus mStatus; + + F64 mSleepTime; + + bool mCanCut; + bool mCanCopy; + bool mCanPaste; + + std::string mMediaName; + std::string mMediaDescription; + + ///////////////////////////////////////// + // media_browser class + std::string mNavigateURI; + S32 mNavigateResultCode; + std::string mNavigateResultString; + bool mHistoryBackAvailable; + bool mHistoryForwardAvailable; + std::string mStatusText; + int mProgressPercent; + std::string mLocation; + std::string mClickURL; + std::string mClickTarget; + + ///////////////////////////////////////// + // media_time class + F64 mCurrentTime; + F64 mDuration; + F64 mCurrentRate; + +}; + +#endif // LL_LLPLUGINCLASSMEDIA_H diff --git a/indra/llplugin/llpluginclassmediaowner.h b/indra/llplugin/llpluginclassmediaowner.h new file mode 100644 index 0000000000..3ae176cbeb --- /dev/null +++ b/indra/llplugin/llpluginclassmediaowner.h @@ -0,0 +1,79 @@ +/** + * @file llpluginclassmediaowner.h + * @brief LLPluginClassMedia handles interaction with a plugin which knows about the "media" message class. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINCLASSMEDIAOWNER_H +#define LL_LLPLUGINCLASSMEDIAOWNER_H + +#include "llgl.h" +#include "llpluginprocessparent.h" +#include "llrect.h" +#include <queue> + +class LLPluginClassMedia; + +class LLPluginClassMediaOwner +{ +public: + typedef enum + { + MEDIA_EVENT_CONTENT_UPDATED, // contents/dirty rect have updated + MEDIA_EVENT_TIME_DURATION_UPDATED, // current time and/or duration have updated + MEDIA_EVENT_SIZE_CHANGED, // media size has changed + MEDIA_EVENT_CURSOR_CHANGED, // plugin has requested a cursor change + + MEDIA_EVENT_NAVIGATE_BEGIN, // browser has begun navigation + MEDIA_EVENT_NAVIGATE_COMPLETE, // browser has finished navigation + MEDIA_EVENT_PROGRESS_UPDATED, // browser has updated loading progress + MEDIA_EVENT_STATUS_TEXT_CHANGED, // browser has updated the status text + MEDIA_EVENT_LOCATION_CHANGED, // browser location (URL) has changed (maybe due to internal navagation/frames/etc) + MEDIA_EVENT_CLICK_LINK_HREF, // I'm not entirely sure what the semantics of these two are + MEDIA_EVENT_CLICK_LINK_NOFOLLOW, + + MEDIA_EVENT_PLUGIN_FAILED // The plugin failed to launch or died unexpectedly + + } EMediaEvent; + + typedef enum + { + MEDIA_NONE, // Uninitialized -- no useful state + MEDIA_LOADING, // loading or navigating + MEDIA_LOADED, // navigation/preroll complete + MEDIA_ERROR, // navigation/preroll failed + MEDIA_PLAYING, // playing (only for time-based media) + MEDIA_PAUSED, // paused (only for time-based media) + + } EMediaStatus; + + virtual ~LLPluginClassMediaOwner() {}; + virtual void handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent /*event*/) {}; +}; + +#endif // LL_LLPLUGINCLASSMEDIAOWNER_H diff --git a/indra/llplugin/llplugininstance.cpp b/indra/llplugin/llplugininstance.cpp new file mode 100644 index 0000000000..58fb792d0d --- /dev/null +++ b/indra/llplugin/llplugininstance.cpp @@ -0,0 +1,140 @@ +/** + * @file llplugininstance.cpp + * @brief LLPluginInstance handles loading the dynamic library of a plugin and setting up its entry points for message passing. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llplugininstance.h" + +#include "llapr.h" + +//virtual +LLPluginInstanceMessageListener::~LLPluginInstanceMessageListener() +{ +} + +const char *LLPluginInstance::PLUGIN_INIT_FUNCTION_NAME = "LLPluginInitEntryPoint"; + +LLPluginInstance::LLPluginInstance(LLPluginInstanceMessageListener *owner) : + mDSOHandle(NULL), + mPluginUserData(NULL), + mPluginSendMessageFunction(NULL) +{ + mOwner = owner; +} + +LLPluginInstance::~LLPluginInstance() +{ + if(mDSOHandle != NULL) + { + apr_dso_unload(mDSOHandle); + mDSOHandle = NULL; + } +} + +int LLPluginInstance::load(std::string &plugin_file) +{ + pluginInitFunction init_function = NULL; + + int result = apr_dso_load(&mDSOHandle, + plugin_file.c_str(), + gAPRPoolp); + if(result != APR_SUCCESS) + { + char buf[1024]; + apr_dso_error(mDSOHandle, buf, sizeof(buf)); + + LL_WARNS("Plugin") << "apr_dso_load of " << plugin_file << " failed with error " << result << " , additional info string: " << buf << LL_ENDL; + + } + + if(result == APR_SUCCESS) + { + result = apr_dso_sym((apr_dso_handle_sym_t*)&init_function, + mDSOHandle, + PLUGIN_INIT_FUNCTION_NAME); + + if(result != APR_SUCCESS) + { + LL_WARNS("Plugin") << "apr_dso_sym failed with error " << result << LL_ENDL; + } + } + + if(result == APR_SUCCESS) + { + result = init_function(staticReceiveMessage, (void*)this, &mPluginSendMessageFunction, &mPluginUserData); + + if(result != APR_SUCCESS) + { + LL_WARNS("Plugin") << "call to init function failed with error " << result << LL_ENDL; + } + } + + return (int)result; +} + +void LLPluginInstance::sendMessage(const std::string &message) +{ + if(mPluginSendMessageFunction) + { + LL_DEBUGS("Plugin") << "sending message to plugin: \"" << message << "\"" << LL_ENDL; + mPluginSendMessageFunction(message.c_str(), &mPluginUserData); + } + else + { + LL_WARNS("Plugin") << "dropping message: \"" << message << "\"" << LL_ENDL; + } +} + +void LLPluginInstance::idle(void) +{ +} + +// static +void LLPluginInstance::staticReceiveMessage(const char *message_string, void **user_data) +{ + // TODO: validate that the user_data argument is still a valid LLPluginInstance pointer + // we could also use a key that's looked up in a map (instead of a direct pointer) for safety, but that's probably overkill + LLPluginInstance *self = (LLPluginInstance*)*user_data; + self->receiveMessage(message_string); +} + +void LLPluginInstance::receiveMessage(const char *message_string) +{ + if(mOwner) + { + LL_DEBUGS("Plugin") << "processing incoming message: \"" << message_string << "\"" << LL_ENDL; + mOwner->receivePluginMessage(message_string); + } + else + { + LL_WARNS("Plugin") << "dropping incoming message: \"" << message_string << "\"" << LL_ENDL; + } +} diff --git a/indra/llplugin/llplugininstance.h b/indra/llplugin/llplugininstance.h new file mode 100644 index 0000000000..ba569df10c --- /dev/null +++ b/indra/llplugin/llplugininstance.h @@ -0,0 +1,88 @@ +/** + * @file llplugininstance.h + * @brief LLPluginInstance handles loading the dynamic library of a plugin and setting up its entry points for message passing. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGININSTANCE_H +#define LL_LLPLUGININSTANCE_H + +#include "llstring.h" +#include "llapr.h" + +#include "apr_dso.h" + +class LLPluginInstanceMessageListener +{ +public: + virtual ~LLPluginInstanceMessageListener(); + virtual void receivePluginMessage(const std::string &message) = 0; +}; + +class LLPluginInstance +{ + LOG_CLASS(LLPluginInstance); +public: + LLPluginInstance(LLPluginInstanceMessageListener *owner); + virtual ~LLPluginInstance(); + + // Load a plugin dll/dylib/so + // Returns 0 if successful, APR error code or error code returned from the plugin's init function on failure. + int load(std::string &plugin_file); + + // Sends a message to the plugin. + void sendMessage(const std::string &message); + + // send_count is the maximum number of message to process from the send queue. If negative, it will drain the queue completely. + // The receive queue is always drained completely. + // Returns the total number of messages processed from both queues. + void idle(void); + + // this is the signature of the "send a message" function. + // message_string is a null-terminated C string + // user_data is the opaque reference that the callee supplied during setup. + typedef void (*sendMessageFunction) (const char *message_string, void **user_data); + + // signature of the plugin init function + typedef int (*pluginInitFunction) (sendMessageFunction host_send_func, void *host_user_data, sendMessageFunction *plugin_send_func, void **plugin_user_data); + + static const char *PLUGIN_INIT_FUNCTION_NAME; + +private: + static void staticReceiveMessage(const char *message_string, void **user_data); + void receiveMessage(const char *message_string); + + apr_dso_handle_t *mDSOHandle; + + void *mPluginUserData; + sendMessageFunction mPluginSendMessageFunction; + + LLPluginInstanceMessageListener *mOwner; +}; + +#endif // LL_LLPLUGININSTANCE_H diff --git a/indra/llplugin/llpluginmessage.cpp b/indra/llplugin/llpluginmessage.cpp new file mode 100644 index 0000000000..bfabc5b7ca --- /dev/null +++ b/indra/llplugin/llpluginmessage.cpp @@ -0,0 +1,249 @@ +/** + * @file llpluginmessage.cpp + * @brief LLPluginMessage encapsulates the serialization/deserialization of messages passed to and from plugins. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llpluginmessage.h" +#include "llsdserialize.h" + +LLPluginMessage::LLPluginMessage() +{ +} + +LLPluginMessage::LLPluginMessage(const std::string &message_class, const std::string &message_name) +{ + setMessage(message_class, message_name); +} + + +LLPluginMessage::~LLPluginMessage() +{ +} + +void LLPluginMessage::clear() +{ + mMessage = LLSD::emptyMap(); + mMessage["params"] = LLSD::emptyMap(); +} + +void LLPluginMessage::setMessage(const std::string &message_class, const std::string &message_name) +{ + clear(); + mMessage["class"] = message_class; + mMessage["name"] = message_name; +} + +void LLPluginMessage::setValue(const std::string &key, const std::string &value) +{ + mMessage["params"][key] = value; +} + +void LLPluginMessage::setValueLLSD(const std::string &key, const LLSD &value) +{ + mMessage["params"][key] = value; +} + +void LLPluginMessage::setValueS32(const std::string &key, S32 value) +{ + mMessage["params"][key] = value; +} + +void LLPluginMessage::setValueU32(const std::string &key, U32 value) +{ + std::stringstream temp; + temp << "0x" << std::hex << value; + setValue(key, temp.str()); +} + +void LLPluginMessage::setValueBoolean(const std::string &key, bool value) +{ + mMessage["params"][key] = value; +} + +void LLPluginMessage::setValueReal(const std::string &key, F64 value) +{ + mMessage["params"][key] = value; +} + +std::string LLPluginMessage::getClass(void) const +{ + return mMessage["class"]; +} + +std::string LLPluginMessage::getName(void) const +{ + return mMessage["name"]; +} + +bool LLPluginMessage::hasValue(const std::string &key) const +{ + bool result = false; + + if(mMessage["params"].has(key)) + { + result = true; + } + + return result; +} + +std::string LLPluginMessage::getValue(const std::string &key) const +{ + std::string result; + + if(mMessage["params"].has(key)) + { + result = mMessage["params"][key].asString(); + } + + return result; +} + +LLSD LLPluginMessage::getValueLLSD(const std::string &key) const +{ + LLSD result; + + if(mMessage["params"].has(key)) + { + result = mMessage["params"][key]; + } + + return result; +} + +S32 LLPluginMessage::getValueS32(const std::string &key) const +{ + S32 result = 0; + + if(mMessage["params"].has(key)) + { + result = mMessage["params"][key].asInteger(); + } + + return result; +} + +U32 LLPluginMessage::getValueU32(const std::string &key) const +{ + U32 result = 0; + + if(mMessage["params"].has(key)) + { + std::string value = mMessage["params"][key].asString(); + + result = (U32)strtoul(value.c_str(), NULL, 16); + } + + return result; +} + +bool LLPluginMessage::getValueBoolean(const std::string &key) const +{ + bool result = false; + + if(mMessage["params"].has(key)) + { + result = mMessage["params"][key].asBoolean(); + } + + return result; +} + +F64 LLPluginMessage::getValueReal(const std::string &key) const +{ + F64 result = 0.0f; + + if(mMessage["params"].has(key)) + { + result = mMessage["params"][key].asReal(); + } + + return result; +} + +std::string LLPluginMessage::generate(void) const +{ + std::ostringstream result; + + // Pretty XML may be slightly easier to deal with while debugging... +// LLSDSerialize::toXML(mMessage, result); + LLSDSerialize::toPrettyXML(mMessage, result); + + return result.str(); +} + + +int LLPluginMessage::parse(const std::string &message) +{ + // clear any previous state + clear(); + + std::istringstream input(message); + + S32 parse_result = LLSDSerialize::fromXML(mMessage, input); + + return (int)parse_result; +} + + +LLPluginMessageListener::~LLPluginMessageListener() +{ + // TODO: should listeners have a way to ensure they're removed from dispatcher lists when deleted? +} + + +LLPluginMessageDispatcher::~LLPluginMessageDispatcher() +{ + +} + +void LLPluginMessageDispatcher::addPluginMessageListener(LLPluginMessageListener *listener) +{ + mListeners.insert(listener); +} + +void LLPluginMessageDispatcher::removePluginMessageListener(LLPluginMessageListener *listener) +{ + mListeners.erase(listener); +} + +void LLPluginMessageDispatcher::dispatchPluginMessage(const LLPluginMessage &message) +{ + for (listener_set_t::iterator it = mListeners.begin(); + it != mListeners.end(); + ) + { + LLPluginMessageListener* listener = *it; + listener->receivePluginMessage(message); + // In case something deleted an entry. + it = mListeners.upper_bound(listener); + } +} diff --git a/indra/llplugin/llpluginmessage.h b/indra/llplugin/llpluginmessage.h new file mode 100644 index 0000000000..a17ec4bb98 --- /dev/null +++ b/indra/llplugin/llpluginmessage.h @@ -0,0 +1,123 @@ +/** + * @file llpluginmessage.h + * @brief LLPluginMessage encapsulates the serialization/deserialization of messages passed to and from plugins. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINMESSAGE_H +#define LL_LLPLUGINMESSAGE_H + +#include "llsd.h" + + +class LLPluginMessage +{ + LOG_CLASS(LLPluginMessage); +public: + LLPluginMessage(); + LLPluginMessage(const std::string &message_class, const std::string &message_name); + ~LLPluginMessage(); + + // reset all internal state + void clear(void); + + // Sets the message class and name + // Also has the side-effect of clearing any key/value pairs in the message. + void setMessage(const std::string &message_class, const std::string &message_name); + + // Sets a key/value pair in the message + void setValue(const std::string &key, const std::string &value); + void setValueLLSD(const std::string &key, const LLSD &value); + void setValueS32(const std::string &key, S32 value); + void setValueU32(const std::string &key, U32 value); + void setValueBoolean(const std::string &key, bool value); + void setValueReal(const std::string &key, F64 value); + + std::string getClass(void) const; + std::string getName(void) const; + + // Returns true if the specified key exists in this message (useful for optional parameters) + bool hasValue(const std::string &key) const; + + // get the value of a particular key as a string. If the key doesn't exist in the message, an empty string will be returned. + std::string getValue(const std::string &key) const; + + // get the value of a particular key as LLSD. If the key doesn't exist in the message, a null LLSD will be returned. + LLSD getValueLLSD(const std::string &key) const; + + // get the value of a key as a S32. If the value wasn't set as a S32, behavior is undefined. + S32 getValueS32(const std::string &key) const; + + // get the value of a key as a U32. Since there isn't an LLSD type for this, we use a hexadecimal string instead. + U32 getValueU32(const std::string &key) const; + + // get the value of a key as a Boolean. + bool getValueBoolean(const std::string &key) const; + + // get the value of a key as a float. + F64 getValueReal(const std::string &key) const; + + // Flatten the message into a string + std::string generate(void) const; + + // Parse an incoming message into component parts + // (this clears out all existing state before starting the parse) + // Returns -1 on failure, otherwise returns the number of key/value pairs in the message. + int parse(const std::string &message); + + +private: + + LLSD mMessage; + +}; + +class LLPluginMessageListener +{ +public: + virtual ~LLPluginMessageListener(); + virtual void receivePluginMessage(const LLPluginMessage &message) = 0; + +}; + +class LLPluginMessageDispatcher +{ +public: + virtual ~LLPluginMessageDispatcher(); + + void addPluginMessageListener(LLPluginMessageListener *); + void removePluginMessageListener(LLPluginMessageListener *); +protected: + void dispatchPluginMessage(const LLPluginMessage &message); + + typedef std::set<LLPluginMessageListener*> listener_set_t; + listener_set_t mListeners; +}; + + +#endif // LL_LLPLUGINMESSAGE_H diff --git a/indra/llplugin/llpluginmessageclasses.h b/indra/llplugin/llpluginmessageclasses.h new file mode 100644 index 0000000000..927fcf2eb2 --- /dev/null +++ b/indra/llplugin/llpluginmessageclasses.h @@ -0,0 +1,56 @@ +/** + * @file llpluginmessageclasses.h + * @brief This file defines the versions of existing message classes for LLPluginMessage. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINMESSAGECLASSES_H +#define LL_LLPLUGINMESSAGECLASSES_H + +// Version strings for each plugin message class. +// Backwards-compatible changes (i.e. changes which only add new messges) should increment the minor version (i.e. "1.0" -> "1.1"). +// Non-backwards-compatible changes (which delete messages or change their semantics) should increment the major version (i.e. "1.1" -> "2.0"). +// Plugins will supply the set of message classes they understand, with version numbers, as part of their init_response message. +// The contents and semantics of the base:init message must NEVER change in a non-backwards-compatible way, as a special case. + +#define LLPLUGIN_MESSAGE_CLASS_INTERNAL "internal" +#define LLPLUGIN_MESSAGE_CLASS_INTERNAL_VERSION "1.0" + +#define LLPLUGIN_MESSAGE_CLASS_BASE "base" +#define LLPLUGIN_MESSAGE_CLASS_BASE_VERSION "1.0" + +#define LLPLUGIN_MESSAGE_CLASS_MEDIA "media" +#define LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION "1.0" + +#define LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER "media_browser" +#define LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION "1.0" + +#define LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME "media_time" +#define LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION "1.0" + +#endif // LL_LLPLUGINMESSAGECLASSES_H diff --git a/indra/llplugin/llpluginmessagepipe.cpp b/indra/llplugin/llpluginmessagepipe.cpp new file mode 100644 index 0000000000..31ea138912 --- /dev/null +++ b/indra/llplugin/llpluginmessagepipe.cpp @@ -0,0 +1,315 @@ +/** + * @file llpluginmessagepipe.cpp + * @brief Classes that implement connections from the plugin system to pipes/pumps. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llpluginmessagepipe.h" +#include "llbufferstream.h" + +#include "llapr.h" + +static const char MESSAGE_DELIMITER = '\0'; + +LLPluginMessagePipeOwner::LLPluginMessagePipeOwner() : + mMessagePipe(NULL), + mSocketError(APR_SUCCESS) +{ +} + +// virtual +LLPluginMessagePipeOwner::~LLPluginMessagePipeOwner() +{ + killMessagePipe(); +} + +// virtual +apr_status_t LLPluginMessagePipeOwner::socketError(apr_status_t error) +{ + mSocketError = error; + return error; +}; + +//virtual +void LLPluginMessagePipeOwner::setMessagePipe(LLPluginMessagePipe *read_pipe) +{ + // Save a reference to this pipe + mMessagePipe = read_pipe; +} + +bool LLPluginMessagePipeOwner::canSendMessage(void) +{ + return (mMessagePipe != NULL); +} + +bool LLPluginMessagePipeOwner::writeMessageRaw(const std::string &message) +{ + bool result = true; + if(mMessagePipe != NULL) + { + result = mMessagePipe->addMessage(message); + } + else + { + LL_WARNS("Plugin") << "dropping message: " << message << LL_ENDL; + result = false; + } + + return result; +} + +void LLPluginMessagePipeOwner::killMessagePipe(void) +{ + if(mMessagePipe != NULL) + { + delete mMessagePipe; + mMessagePipe = NULL; + } +} + +LLPluginMessagePipe::LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket) +{ + mOwner = owner; + mOwner->setMessagePipe(this); + mSocket = socket; +} + +LLPluginMessagePipe::~LLPluginMessagePipe() +{ + if(mOwner != NULL) + { + mOwner->setMessagePipe(NULL); + } +} + +bool LLPluginMessagePipe::addMessage(const std::string &message) +{ + // queue the message for later output + mOutput += message; + mOutput += MESSAGE_DELIMITER; // message separator + + return true; +} + +void LLPluginMessagePipe::clearOwner(void) +{ + // The owner is done with this pipe. The next call to process_impl should send any remaining data and exit. + mOwner = NULL; +} + +void LLPluginMessagePipe::setSocketTimeout(apr_interval_time_t timeout_usec) +{ + // We never want to sleep forever, so force negative timeouts to become non-blocking. + + // according to this page: http://dev.ariel-networks.com/apr/apr-tutorial/html/apr-tutorial-13.html + // blocking/non-blocking with apr sockets is somewhat non-portable. + + if(timeout_usec <= 0) + { + // Make the socket non-blocking + apr_socket_opt_set(mSocket->getSocket(), APR_SO_NONBLOCK, 1); + apr_socket_timeout_set(mSocket->getSocket(), 0); + } + else + { + // Make the socket blocking-with-timeout + apr_socket_opt_set(mSocket->getSocket(), APR_SO_NONBLOCK, 1); + apr_socket_timeout_set(mSocket->getSocket(), timeout_usec); + } +} + +bool LLPluginMessagePipe::pump(F64 timeout) +{ + bool result = true; + + if(mSocket) + { + apr_status_t status; + apr_size_t size; + + if(!mOutput.empty()) + { + // write any outgoing messages + size = (apr_size_t)mOutput.size(); + + setSocketTimeout(0); + +// LL_INFOS("Plugin") << "before apr_socket_send, size = " << size << LL_ENDL; + + status = apr_socket_send( + mSocket->getSocket(), + (const char*)mOutput.data(), + &size); + +// LL_INFOS("Plugin") << "after apr_socket_send, size = " << size << LL_ENDL; + + if(status == APR_SUCCESS) + { + // success + mOutput = mOutput.substr(size); + } + else if(APR_STATUS_IS_EAGAIN(status)) + { + // Socket buffer is full... + // remove the written part from the buffer and try again later. + mOutput = mOutput.substr(size); + } + else + { + // some other error + // Treat this as fatal. + ll_apr_warn_status(status); + + if(mOwner) + { + mOwner->socketError(status); + } + result = false; + } + } + + // FIXME: For some reason, the apr timeout stuff isn't working properly on windows. + // Until such time as we figure out why, don't try to use the socket timeout -- just sleep here instead. +#if LL_WINDOWS + if(result) + { + if(timeout != 0.0f) + { + ms_sleep((int)(timeout * 1000.0f)); + timeout = 0.0f; + } + } +#endif + + // Check for incoming messages + if(result) + { + char input_buf[1024]; + apr_size_t request_size; + + // Start out by reading one byte, so that any data received will wake us up. + request_size = 1; + + // and use the timeout so we'll sleep if no data is available. + setSocketTimeout((apr_interval_time_t)(timeout * 1000000)); + + while(1) + { + size = request_size; + +// LL_INFOS("Plugin") << "before apr_socket_recv, size = " << size << LL_ENDL; + + status = apr_socket_recv( + mSocket->getSocket(), + input_buf, + &size); + +// LL_INFOS("Plugin") << "after apr_socket_recv, size = " << size << LL_ENDL; + + if(size > 0) + mInput.append(input_buf, size); + + if(status == APR_SUCCESS) + { +// llinfos << "success, read " << size << llendl; + + if(size != request_size) + { + // This was a short read, so we're done. + break; + } + } + else if(APR_STATUS_IS_TIMEUP(status)) + { +// llinfos << "TIMEUP, read " << size << llendl; + + // Timeout was hit. Since the initial read is 1 byte, this should never be a partial read. + break; + } + else if(APR_STATUS_IS_EAGAIN(status)) + { +// llinfos << "EAGAIN, read " << size << llendl; + + // We've been doing partial reads, and we're done now. + break; + } + else + { + // some other error + // Treat this as fatal. + ll_apr_warn_status(status); + + if(mOwner) + { + mOwner->socketError(status); + } + result = false; + break; + } + + // Second and subsequent reads should not use the timeout + setSocketTimeout(0); + // and should try to fill the input buffer + request_size = sizeof(input_buf); + } + + processInput(); + } + } + + if(!result) + { + // If we got an error, we're done. + LL_INFOS("Plugin") << "Error from socket, cleaning up." << LL_ENDL; + delete this; + } + + return result; +} + +void LLPluginMessagePipe::processInput(void) +{ + // Look for input delimiter(s) in the input buffer. + int start = 0; + int delim; + while((delim = mInput.find(MESSAGE_DELIMITER, start)) != std::string::npos) + { + // Let the owner process this message + mOwner->receiveMessageRaw(mInput.substr(start, delim - start)); + + start = delim + 1; + } + + // Remove delivered messages from the input buffer. + if(start != 0) + mInput = mInput.substr(start); + +} + diff --git a/indra/llplugin/llpluginmessagepipe.h b/indra/llplugin/llpluginmessagepipe.h new file mode 100644 index 0000000000..4eb6575bd4 --- /dev/null +++ b/indra/llplugin/llpluginmessagepipe.h @@ -0,0 +1,91 @@ +/** + * @file llpluginmessagepipe.h + * @brief Classes that implement connections from the plugin system to pipes/pumps. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINMESSAGEPIPE_H +#define LL_LLPLUGINMESSAGEPIPE_H + +#include "lliosocket.h" + +class LLPluginMessagePipe; + +// Inherit from this to be able to receive messages from the LLPluginMessagePipe +class LLPluginMessagePipeOwner +{ + LOG_CLASS(LLPluginMessagePipeOwner); +public: + LLPluginMessagePipeOwner(); + virtual ~LLPluginMessagePipeOwner(); + // called with incoming messages + virtual void receiveMessageRaw(const std::string &message) = 0; + // called when the socket has an error + virtual apr_status_t socketError(apr_status_t error); + + // called from LLPluginMessagePipe to manage the connection with LLPluginMessagePipeOwner -- do not use! + virtual void setMessagePipe(LLPluginMessagePipe *message_pipe) ; + +protected: + // returns false if writeMessageRaw() would drop the message + bool canSendMessage(void); + // call this to send a message over the pipe + bool writeMessageRaw(const std::string &message); + // call this to close the pipe + void killMessagePipe(void); + + LLPluginMessagePipe *mMessagePipe; + apr_status_t mSocketError; +}; + +class LLPluginMessagePipe +{ + LOG_CLASS(LLPluginMessagePipe); +public: + LLPluginMessagePipe(LLPluginMessagePipeOwner *owner, LLSocket::ptr_t socket); + virtual ~LLPluginMessagePipe(); + + bool addMessage(const std::string &message); + void clearOwner(void); + + bool pump(F64 timeout = 0.0f); + +protected: + void processInput(void); + + // used internally by pump() + void setSocketTimeout(apr_interval_time_t timeout_usec); + + std::string mInput; + std::string mOutput; + + LLPluginMessagePipeOwner *mOwner; + LLSocket::ptr_t mSocket; +}; + +#endif // LL_LLPLUGINMESSAGE_H diff --git a/indra/llplugin/llpluginprocesschild.cpp b/indra/llplugin/llpluginprocesschild.cpp new file mode 100644 index 0000000000..293dea6fe1 --- /dev/null +++ b/indra/llplugin/llpluginprocesschild.cpp @@ -0,0 +1,466 @@ +/** + * @file llpluginprocesschild.cpp + * @brief LLPluginProcessChild handles the child side of the external-process plugin API. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llpluginprocesschild.h" +#include "llplugininstance.h" +#include "llpluginmessagepipe.h" +#include "llpluginmessageclasses.h" + +static const F32 HEARTBEAT_SECONDS = 1.0f; + +LLPluginProcessChild::LLPluginProcessChild() +{ + mInstance = NULL; + mSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); + mSleepTime = 1.0f / 100.0f; // default: send idle messages at 100Hz +} + +LLPluginProcessChild::~LLPluginProcessChild() +{ + if(mInstance != NULL) + { + sendMessageToPlugin(LLPluginMessage("base", "cleanup")); + delete mInstance; + mInstance = NULL; + } +} + +void LLPluginProcessChild::killSockets(void) +{ + killMessagePipe(); + mSocket.reset(); +} + +void LLPluginProcessChild::init(U32 launcher_port) +{ + mLauncherHost = LLHost("127.0.0.1", launcher_port); + setState(STATE_INITIALIZED); +} + +void LLPluginProcessChild::idle(void) +{ + bool idle_again; + do + { + if(mSocketError != APR_SUCCESS) + { + LL_INFOS("Plugin") << "message pipe is in error state, moving to STATE_ERROR"<< LL_ENDL; + setState(STATE_ERROR); + } + + if((mState > STATE_INITIALIZED) && (mMessagePipe == NULL)) + { + // The pipe has been closed -- we're done. + // TODO: This could be slightly more subtle, but I'm not sure it needs to be. + LL_INFOS("Plugin") << "message pipe went away, moving to STATE_ERROR"<< LL_ENDL; + setState(STATE_ERROR); + } + + // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). + // USE THIS CAREFULLY, since it can starve other code. Specifically make sure there's no way to get into a closed cycle and never return. + // When in doubt, don't do it. + idle_again = false; + + if(mInstance != NULL) + { + // Provide some time to the plugin + mInstance->idle(); + } + + switch(mState) + { + case STATE_UNINITIALIZED: + break; + + case STATE_INITIALIZED: + if(mSocket->blockingConnect(mLauncherHost)) + { + // This automatically sets mMessagePipe + new LLPluginMessagePipe(this, mSocket); + + setState(STATE_CONNECTED); + } + else + { + // connect failed + setState(STATE_ERROR); + } + break; + + case STATE_CONNECTED: + sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "hello")); + setState(STATE_PLUGIN_LOADING); + break; + + case STATE_PLUGIN_LOADING: + if(!mPluginFile.empty()) + { + mInstance = new LLPluginInstance(this); + if(mInstance->load(mPluginFile) == 0) + { + mHeartbeat.start(); + mHeartbeat.setTimerExpirySec(HEARTBEAT_SECONDS); + setState(STATE_PLUGIN_LOADED); + } + else + { + setState(STATE_ERROR); + } + } + break; + + case STATE_PLUGIN_LOADED: + setState(STATE_PLUGIN_INITIALIZING); + sendMessageToPlugin(LLPluginMessage("base", "init")); + break; + + case STATE_PLUGIN_INITIALIZING: + // waiting for init_response... + break; + + case STATE_RUNNING: + if(mInstance != NULL) + { + // Provide some time to the plugin + LLPluginMessage message("base", "idle"); + message.setValueReal("time", mSleepTime); + sendMessageToPlugin(message); + + mInstance->idle(); + + if(mHeartbeat.checkExpirationAndReset(HEARTBEAT_SECONDS)) + { + // This just proves that we're not stuck down inside the plugin code. + sendMessageToParent(LLPluginMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "heartbeat")); + } + } + // receivePluginMessage will transition to STATE_UNLOADING + break; + + case STATE_UNLOADING: + if(mInstance != NULL) + { + sendMessageToPlugin(LLPluginMessage("base", "cleanup")); + delete mInstance; + mInstance = NULL; + } + setState(STATE_UNLOADED); + break; + + case STATE_UNLOADED: + killSockets(); + setState(STATE_DONE); + break; + + case STATE_ERROR: + // Close the socket to the launcher + killSockets(); + // TODO: Where do we go from here? Just exit()? + setState(STATE_DONE); + break; + + case STATE_DONE: + // just sit here. + break; + } + + } while (idle_again); +} + +void LLPluginProcessChild::sleep(F64 seconds) +{ + if(mMessagePipe) + { + mMessagePipe->pump(seconds); + } + else + { + ms_sleep((int)(seconds * 1000.0f)); + } +} + +void LLPluginProcessChild::pump(void) +{ + if(mMessagePipe) + { + mMessagePipe->pump(0.0f); + } + else + { + // Should we warn here? + } +} + + +bool LLPluginProcessChild::isRunning(void) +{ + bool result = false; + + if(mState == STATE_RUNNING) + result = true; + + return result; +} + +bool LLPluginProcessChild::isDone(void) +{ + bool result = false; + + switch(mState) + { + case STATE_DONE: + result = true; + break; + default: + break; + } + + return result; +} + +void LLPluginProcessChild::sendMessageToPlugin(const LLPluginMessage &message) +{ + std::string buffer = message.generate(); + + LL_DEBUGS("Plugin") << "Sending to plugin: " << buffer << LL_ENDL; + + mInstance->sendMessage(buffer); +} + +void LLPluginProcessChild::sendMessageToParent(const LLPluginMessage &message) +{ + std::string buffer = message.generate(); + + LL_DEBUGS("Plugin") << "Sending to parent: " << buffer << LL_ENDL; + + writeMessageRaw(buffer); +} + +void LLPluginProcessChild::receiveMessageRaw(const std::string &message) +{ + // Incoming message from the TCP Socket + + LL_DEBUGS("Plugin") << "Received from parent: " << message << LL_ENDL; + + bool passMessage = true; + + // FIXME: how should we handle queueing here? + + { + // Decode this message + LLPluginMessage parsed; + parsed.parse(message); + + std::string message_class = parsed.getClass(); + if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) + { + passMessage = false; + + std::string message_name = parsed.getName(); + if(message_name == "load_plugin") + { + mPluginFile = parsed.getValue("file"); + } + else if(message_name == "shm_add") + { + std::string name = parsed.getValue("name"); + size_t size = (size_t)parsed.getValueS32("size"); + + sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); + if(iter != mSharedMemoryRegions.end()) + { + // Need to remove the old region first + LL_WARNS("Plugin") << "Adding a duplicate shared memory segment!" << LL_ENDL; + } + else + { + // This is a new region + LLPluginSharedMemory *region = new LLPluginSharedMemory; + if(region->attach(name, size)) + { + mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region)); + + std::stringstream addr; + addr << region->getMappedAddress(); + + // Send the add notification to the plugin + LLPluginMessage message("base", "shm_added"); + message.setValue("name", name); + message.setValueS32("size", (S32)size); + // shm address is split into 2x32bit values because LLSD doesn't serialize 64bit values and we need to support 64-bit addressing. + void * address = region->getMappedAddress(); + U32 address_lo = (U32)address; + U32 address_hi = (U32)(U64(address) / (U64(1)<<31)); + message.setValueU32("address", address_lo); + message.setValueU32("address_1", address_hi); + sendMessageToPlugin(message); + + // and send the response to the parent + message.setMessage(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add_response"); + message.setValue("name", name); + sendMessageToParent(message); + } + else + { + LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL; + } + } + + } + else if(message_name == "shm_remove") + { + std::string name = parsed.getValue("name"); + sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); + if(iter != mSharedMemoryRegions.end()) + { + // forward the remove request to the plugin -- its response will trigger us to detach the segment. + LLPluginMessage message("base", "shm_remove"); + message.setValue("name", name); + sendMessageToPlugin(message); + } + else + { + LL_WARNS("Plugin") << "shm_remove for unknown memory segment!" << LL_ENDL; + } + } + else if(message_name == "sleep_time") + { + mSleepTime = parsed.getValueReal("time"); + } + else if(message_name == "crash") + { + // Crash the plugin + LL_ERRS("Plugin") << "Plugin crash requested." << LL_ENDL; + } + else if(message_name == "hang") + { + // Hang the plugin + LL_WARNS("Plugin") << "Plugin hang requested." << LL_ENDL; + while(1) + { + // wheeeeeeeee...... + } + } + else + { + LL_WARNS("Plugin") << "Unknown internal message from parent: " << message_name << LL_ENDL; + } + } + } + + if(passMessage && mInstance != NULL) + { + mInstance->sendMessage(message); + } +} + +/* virtual */ +void LLPluginProcessChild::receivePluginMessage(const std::string &message) +{ + LL_DEBUGS("Plugin") << "Received from plugin: " << message << LL_ENDL; + + // Incoming message from the plugin instance + bool passMessage = true; + + // FIXME: how should we handle queueing here? + + // Intercept certain base messages (responses to ones sent by this class) + { + // Decode this message + LLPluginMessage parsed; + parsed.parse(message); + std::string message_class = parsed.getClass(); + if(message_class == "base") + { + std::string message_name = parsed.getName(); + if(message_name == "init_response") + { + // The plugin has finished initializing. + setState(STATE_RUNNING); + + // Don't pass this message up to the parent + passMessage = false; + + LLPluginMessage new_message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin_response"); + LLSD versions = parsed.getValueLLSD("versions"); + new_message.setValueLLSD("versions", versions); + + if(parsed.hasValue("plugin_version")) + { + std::string plugin_version = parsed.getValue("plugin_version"); + new_message.setValueLLSD("plugin_version", plugin_version); + } + + // Let the parent know it's loaded and initialized. + sendMessageToParent(new_message); + } + else if(message_name == "shm_remove_response") + { + // Don't pass this message up to the parent + passMessage = false; + + std::string name = parsed.getValue("name"); + sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); + if(iter != mSharedMemoryRegions.end()) + { + // detach the shared memory region + iter->second->detach(); + + // and remove it from our map + mSharedMemoryRegions.erase(iter); + + // Finally, send the response to the parent. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove_response"); + message.setValue("name", name); + sendMessageToParent(message); + } + else + { + LL_WARNS("Plugin") << "shm_remove_response for unknown memory segment!" << LL_ENDL; + } + } + } + } + + if(passMessage) + { + writeMessageRaw(message); + } +} + + +void LLPluginProcessChild::setState(EState state) +{ + LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL; + mState = state; +}; diff --git a/indra/llplugin/llpluginprocesschild.h b/indra/llplugin/llpluginprocesschild.h new file mode 100644 index 0000000000..f92905e8bd --- /dev/null +++ b/indra/llplugin/llpluginprocesschild.h @@ -0,0 +1,108 @@ +/** + * @file llpluginprocesschild.h + * @brief LLPluginProcessChild handles the child side of the external-process plugin API. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINPROCESSCHILD_H +#define LL_LLPLUGINPROCESSCHILD_H + +#include "llpluginmessage.h" +#include "llpluginmessagepipe.h" +#include "llplugininstance.h" +#include "llhost.h" +#include "llpluginsharedmemory.h" + +class LLPluginInstance; + +class LLPluginProcessChild: public LLPluginMessagePipeOwner, public LLPluginInstanceMessageListener +{ + LOG_CLASS(LLPluginProcessChild); +public: + LLPluginProcessChild(); + ~LLPluginProcessChild(); + + void init(U32 launcher_port); + void idle(void); + void sleep(F64 seconds); + void pump(); + + // returns true if the plugin is in the steady state (processing messages) + bool isRunning(void); + + // returns true if the plugin is unloaded or we're in an unrecoverable error state. + bool isDone(void); + + void killSockets(void); + + F64 getSleepTime(void) const { return mSleepTime; }; + + void sendMessageToPlugin(const LLPluginMessage &message); + void sendMessageToParent(const LLPluginMessage &message); + + // Inherited from LLPluginMessagePipeOwner + /* virtual */ void receiveMessageRaw(const std::string &message); + + // Inherited from LLPluginInstanceMessageListener + /* virtual */ void receivePluginMessage(const std::string &message); + +private: + + enum EState + { + STATE_UNINITIALIZED, + STATE_INITIALIZED, // init() has been called + STATE_CONNECTED, // connected back to launcher + STATE_PLUGIN_LOADING, // plugin library needs to be loaded + STATE_PLUGIN_LOADED, // plugin library has been loaded + STATE_PLUGIN_INITIALIZING, // plugin is processing init message + STATE_RUNNING, // steady state (processing messages) + STATE_UNLOADING, // plugin has sent shutdown_response and needs to be unloaded + STATE_UNLOADED, // plugin has been unloaded + STATE_ERROR, // generic bailout state + STATE_DONE // state machine will sit in this state after either error or normal termination. + }; + EState mState; + void setState(EState state); + + LLHost mLauncherHost; + LLSocket::ptr_t mSocket; + + std::string mPluginFile; + + LLPluginInstance *mInstance; + + typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType; + sharedMemoryRegionsType mSharedMemoryRegions; + + LLTimer mHeartbeat; + F64 mSleepTime; + +}; + +#endif // LL_LLPLUGINPROCESSCHILD_H diff --git a/indra/llplugin/llpluginprocessparent.cpp b/indra/llplugin/llpluginprocessparent.cpp new file mode 100644 index 0000000000..f18a117dde --- /dev/null +++ b/indra/llplugin/llpluginprocessparent.cpp @@ -0,0 +1,658 @@ +/** + * @file llpluginprocessparent.cpp + * @brief LLPluginProcessParent handles the parent side of the external-process plugin API. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llpluginprocessparent.h" +#include "llpluginmessagepipe.h" +#include "llpluginmessageclasses.h" + +#include "llapr.h" + +// If we don't receive a heartbeat in this many seconds, we declare the plugin locked up. +static const F32 PLUGIN_LOCKED_UP_SECONDS = 10.0f; + +// Somewhat longer timeout for initial launch. +static const F32 PLUGIN_LAUNCH_SECONDS = 20.0f; + +//virtual +LLPluginProcessParentOwner::~LLPluginProcessParentOwner() +{ + +} + +LLPluginProcessParent::LLPluginProcessParent(LLPluginProcessParentOwner *owner) +{ + mOwner = owner; + mBoundPort = 0; + mState = STATE_UNINITIALIZED; + mDisableTimeout = false; +} + +LLPluginProcessParent::~LLPluginProcessParent() +{ + LL_DEBUGS("Plugin") << "destructor" << LL_ENDL; + + // Destroy any remaining shared memory regions + sharedMemoryRegionsType::iterator iter; + while((iter = mSharedMemoryRegions.begin()) != mSharedMemoryRegions.end()) + { + // destroy the shared memory region + iter->second->destroy(); + + // and remove it from our map + mSharedMemoryRegions.erase(iter); + } + + mProcess.kill(); + killSockets(); +} + +void LLPluginProcessParent::killSockets(void) +{ + killMessagePipe(); + mListenSocket.reset(); + mSocket.reset(); +} + +void LLPluginProcessParent::init(const std::string &launcher_filename, const std::string &plugin_filename) +{ + mProcess.setExecutable(launcher_filename); + mPluginFile = plugin_filename; + + setState(STATE_INITIALIZED); +} + +bool LLPluginProcessParent::accept() +{ + bool result = false; + + apr_status_t status = APR_EGENERAL; + apr_socket_t *new_socket = NULL; + + status = apr_socket_accept( + &new_socket, + mListenSocket->getSocket(), + gAPRPoolp); + + + if(status == APR_SUCCESS) + { +// llinfos << "SUCCESS" << llendl; + // Success. Create a message pipe on the new socket + + // we MUST create a new pool for the LLSocket, since it will take ownership of it and delete it in its destructor! + apr_pool_t* new_pool = NULL; + status = apr_pool_create(&new_pool, gAPRPoolp); + + mSocket = LLSocket::create(new_socket, new_pool); + new LLPluginMessagePipe(this, mSocket); + + result = true; + } + else if(APR_STATUS_IS_EAGAIN(status)) + { +// llinfos << "EAGAIN" << llendl; + + // No incoming connections. This is not an error. + status = APR_SUCCESS; + } + else + { +// llinfos << "Error:" << llendl; + ll_apr_warn_status(status); + + // Some other error. + setState(STATE_ERROR); + } + + return result; +} + +void LLPluginProcessParent::idle(void) +{ + bool idle_again; + + do + { + // Give time to network processing + if(mMessagePipe) + { + if(!mMessagePipe->pump()) + { +// LL_WARNS("Plugin") << "Message pipe hit an error state" << LL_ENDL; + setState(STATE_ERROR); + } + } + + if((mSocketError != APR_SUCCESS) && (mState < STATE_ERROR)) + { + // The socket is in an error state -- the plugin is gone. + LL_WARNS("Plugin") << "Socket hit an error state (" << mSocketError << ")" << LL_ENDL; + setState(STATE_ERROR); + } + + // If a state needs to go directly to another state (as a performance enhancement), it can set idle_again to true after calling setState(). + // USE THIS CAREFULLY, since it can starve other code. Specifically make sure there's no way to get into a closed cycle and never return. + // When in doubt, don't do it. + idle_again = false; + switch(mState) + { + case STATE_UNINITIALIZED: + break; + + case STATE_INITIALIZED: + { + + apr_status_t status = APR_SUCCESS; + apr_sockaddr_t* addr = NULL; + mListenSocket = LLSocket::create(gAPRPoolp, LLSocket::STREAM_TCP); + mBoundPort = 0; + + // This code is based on parts of LLSocket::create() in lliosocket.cpp. + + status = apr_sockaddr_info_get( + &addr, + "127.0.0.1", + APR_INET, + 0, // port 0 = ephemeral ("find me a port") + 0, + gAPRPoolp); + + if(ll_apr_warn_status(status)) + { + killSockets(); + setState(STATE_ERROR); + break; + } + + // This allows us to reuse the address on quick down/up. This is unlikely to create problems. + ll_apr_warn_status(apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_REUSEADDR, 1)); + + status = apr_socket_bind(mListenSocket->getSocket(), addr); + if(ll_apr_warn_status(status)) + { + killSockets(); + setState(STATE_ERROR); + break; + } + + // Get the actual port the socket was bound to + { + apr_sockaddr_t* bound_addr = NULL; + if(ll_apr_warn_status(apr_socket_addr_get(&bound_addr, APR_LOCAL, mListenSocket->getSocket()))) + { + killSockets(); + setState(STATE_ERROR); + break; + } + mBoundPort = bound_addr->port; + + if(mBoundPort == 0) + { + LL_WARNS("Plugin") << "Bound port number unknown, bailing out." << LL_ENDL; + + killSockets(); + setState(STATE_ERROR); + break; + } + } + + LL_DEBUGS("Plugin") << "Bound tcp socket to port: " << addr->port << LL_ENDL; + + // Make the listen socket non-blocking + status = apr_socket_opt_set(mListenSocket->getSocket(), APR_SO_NONBLOCK, 1); + if(ll_apr_warn_status(status)) + { + killSockets(); + setState(STATE_ERROR); + break; + } + + apr_socket_timeout_set(mListenSocket->getSocket(), 0); + if(ll_apr_warn_status(status)) + { + killSockets(); + setState(STATE_ERROR); + break; + } + + // If it's a stream based socket, we need to tell the OS + // to keep a queue of incoming connections for ACCEPT. + status = apr_socket_listen( + mListenSocket->getSocket(), + 10); // FIXME: Magic number for queue size + + if(ll_apr_warn_status(status)) + { + killSockets(); + setState(STATE_ERROR); + break; + } + + // If we got here, we're listening. + setState(STATE_LISTENING); + } + break; + + case STATE_LISTENING: + { + // Launch the plugin process. + + // Only argument to the launcher is the port number we're listening on + std::stringstream stream; + stream << mBoundPort; + mProcess.addArgument(stream.str()); + if(mProcess.launch() != 0) + { + setState(STATE_ERROR); + } + else + { + // This will allow us to time out if the process never starts. + mHeartbeat.start(); + mHeartbeat.setTimerExpirySec(PLUGIN_LAUNCH_SECONDS); + setState(STATE_LAUNCHED); + } + } + break; + + case STATE_LAUNCHED: + // waiting for the plugin to connect + if(pluginLockedUpOrQuit()) + { + setState(STATE_ERROR); + } + else + { + // Check for the incoming connection. + if(accept()) + { + // Stop listening on the server port + mListenSocket.reset(); + setState(STATE_CONNECTED); + } + } + break; + + case STATE_CONNECTED: + // waiting for hello message from the plugin + + if(pluginLockedUpOrQuit()) + { + setState(STATE_ERROR); + } + break; + + case STATE_HELLO: + LL_DEBUGS("Plugin") << "received hello message" << llendl; + + // Send the message to load the plugin + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "load_plugin"); + message.setValue("file", mPluginFile); + sendMessage(message); + } + + setState(STATE_LOADING); + break; + + case STATE_LOADING: + // The load_plugin_response message will kick us from here into STATE_RUNNING + if(pluginLockedUpOrQuit()) + { + setState(STATE_ERROR); + } + break; + + case STATE_RUNNING: + if(pluginLockedUpOrQuit()) + { + setState(STATE_ERROR); + } + break; + + case STATE_EXITING: + if(!mProcess.isRunning()) + { + setState(STATE_CLEANUP); + } + else if(pluginLockedUp()) + { + LL_WARNS("Plugin") << "timeout in exiting state, bailing out" << llendl; + setState(STATE_ERROR); + } + break; + + case STATE_ERROR: + if(mOwner != NULL) + { + mOwner->pluginDied(); + } + setState(STATE_CLEANUP); + break; + + case STATE_CLEANUP: + mProcess.kill(); + killSockets(); + setState(STATE_DONE); + break; + + + case STATE_DONE: + // just sit here. + break; + + } + + } while (idle_again); +} + +bool LLPluginProcessParent::isLoading(void) +{ + bool result = false; + + if(mState <= STATE_LOADING) + result = true; + + return result; +} + +bool LLPluginProcessParent::isRunning(void) +{ + bool result = false; + + if(mState == STATE_RUNNING) + result = true; + + return result; +} + +bool LLPluginProcessParent::isDone(void) +{ + bool result = false; + + if(mState == STATE_DONE) + result = true; + + return result; +} + +void LLPluginProcessParent::setSleepTime(F64 sleep_time, bool force_send) +{ + if(force_send || (sleep_time != mSleepTime)) + { + // Cache the time locally + mSleepTime = sleep_time; + + if(canSendMessage()) + { + // and send to the plugin. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "sleep_time"); + message.setValueReal("time", mSleepTime); + sendMessage(message); + } + else + { + // Too early to send -- the load_plugin_response message will trigger us to send mSleepTime later. + } + } +} + +void LLPluginProcessParent::sendMessage(const LLPluginMessage &message) +{ + + std::string buffer = message.generate(); + LL_DEBUGS("Plugin") << "Sending: " << buffer << LL_ENDL; + writeMessageRaw(buffer); +} + + +void LLPluginProcessParent::receiveMessageRaw(const std::string &message) +{ + LL_DEBUGS("Plugin") << "Received: " << message << LL_ENDL; + + // FIXME: should this go into a queue instead? + + LLPluginMessage parsed; + if(parsed.parse(message) != -1) + { + receiveMessage(parsed); + } +} + +void LLPluginProcessParent::receiveMessage(const LLPluginMessage &message) +{ + std::string message_class = message.getClass(); + if(message_class == LLPLUGIN_MESSAGE_CLASS_INTERNAL) + { + // internal messages should be handled here + std::string message_name = message.getName(); + if(message_name == "hello") + { + if(mState == STATE_CONNECTED) + { + // Plugin host has launched. Tell it which plugin to load. + setState(STATE_HELLO); + } + else + { + LL_WARNS("Plugin") << "received hello message in wrong state -- bailing out" << LL_ENDL; + setState(STATE_ERROR); + } + + } + else if(message_name == "load_plugin_response") + { + if(mState == STATE_LOADING) + { + // Plugin has been loaded. + + // Check which message classes/versions the plugin supports. + // TODO: check against current versions + // TODO: kill plugin on major mismatches? + mMessageClassVersions = message.getValueLLSD("versions"); + LLSD::map_iterator iter; + for(iter = mMessageClassVersions.beginMap(); iter != mMessageClassVersions.endMap(); iter++) + { + LL_INFOS("Plugin") << "message class: " << iter->first << " -> version: " << iter->second.asString() << LL_ENDL; + } + + mPluginVersionString = message.getValue("plugin_version"); + + // Send initial sleep time + setSleepTime(mSleepTime, true); + + setState(STATE_RUNNING); + } + else + { + LL_WARNS("Plugin") << "received load_plugin_response message in wrong state -- bailing out" << LL_ENDL; + setState(STATE_ERROR); + } + } + else if(message_name == "heartbeat") + { + // this resets our timer. + mHeartbeat.setTimerExpirySec(PLUGIN_LOCKED_UP_SECONDS); + } + else if(message_name == "shm_add_response") + { + // Nothing to do here. + } + else if(message_name == "shm_remove_response") + { + std::string name = message.getValue("name"); + sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); + + if(iter != mSharedMemoryRegions.end()) + { + // destroy the shared memory region + iter->second->destroy(); + + // and remove it from our map + mSharedMemoryRegions.erase(iter); + } + } + else + { + LL_WARNS("Plugin") << "Unknown internal message from child: " << message_name << LL_ENDL; + } + } + else + { + if(mOwner != NULL) + { + mOwner->receivePluginMessage(message); + } + } +} + +std::string LLPluginProcessParent::addSharedMemory(size_t size) +{ + std::string name; + + LLPluginSharedMemory *region = new LLPluginSharedMemory; + + // This is a new region + if(region->create(size)) + { + name = region->getName(); + + mSharedMemoryRegions.insert(sharedMemoryRegionsType::value_type(name, region)); + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_add"); + message.setValue("name", name); + message.setValueS32("size", (S32)size); + sendMessage(message); + } + else + { + LL_WARNS("Plugin") << "Couldn't create a shared memory segment!" << LL_ENDL; + + // Don't leak + delete region; + } + + return name; +} + +void LLPluginProcessParent::removeSharedMemory(const std::string &name) +{ + sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); + + if(iter != mSharedMemoryRegions.end()) + { + // This segment exists. Send the message to the child to unmap it. The response will cause the parent to unmap our end. + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_INTERNAL, "shm_remove"); + message.setValue("name", name); + sendMessage(message); + } + else + { + LL_WARNS("Plugin") << "Request to remove an unknown shared memory segment." << LL_ENDL; + } +} +size_t LLPluginProcessParent::getSharedMemorySize(const std::string &name) +{ + size_t result = 0; + + sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); + if(iter != mSharedMemoryRegions.end()) + { + result = iter->second->getSize(); + } + + return result; +} +void *LLPluginProcessParent::getSharedMemoryAddress(const std::string &name) +{ + void *result = NULL; + + sharedMemoryRegionsType::iterator iter = mSharedMemoryRegions.find(name); + if(iter != mSharedMemoryRegions.end()) + { + result = iter->second->getMappedAddress(); + } + + return result; +} + +std::string LLPluginProcessParent::getMessageClassVersion(const std::string &message_class) +{ + std::string result; + + if(mMessageClassVersions.has(message_class)) + { + result = mMessageClassVersions[message_class].asString(); + } + + return result; +} + +std::string LLPluginProcessParent::getPluginVersion(void) +{ + return mPluginVersionString; +} + +void LLPluginProcessParent::setState(EState state) +{ + LL_DEBUGS("Plugin") << "setting state to " << state << LL_ENDL; + mState = state; +}; + +bool LLPluginProcessParent::pluginLockedUpOrQuit() +{ + bool result = false; + + if(!mDisableTimeout) + { + if(!mProcess.isRunning()) + { + LL_WARNS("Plugin") << "child exited" << llendl; + result = true; + } + else if(pluginLockedUp()) + { + LL_WARNS("Plugin") << "timeout" << llendl; + result = true; + } + } + + return result; +} + +bool LLPluginProcessParent::pluginLockedUp() +{ + // If the timer has expired, the plugin has locked up. + return mHeartbeat.hasExpired(); +} + diff --git a/indra/llplugin/llpluginprocessparent.h b/indra/llplugin/llpluginprocessparent.h new file mode 100644 index 0000000000..545eb85c9a --- /dev/null +++ b/indra/llplugin/llpluginprocessparent.h @@ -0,0 +1,147 @@ +/** + * @file llpluginprocessparent.h + * @brief LLPluginProcessParent handles the parent side of the external-process plugin API. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINPROCESSPARENT_H +#define LL_LLPLUGINPROCESSPARENT_H + +#include "llprocesslauncher.h" +#include "llpluginmessage.h" +#include "llpluginmessagepipe.h" +#include "llpluginsharedmemory.h" + +#include "lliosocket.h" + +class LLPluginProcessParentOwner +{ +public: + virtual ~LLPluginProcessParentOwner(); + virtual void receivePluginMessage(const LLPluginMessage &message) = 0; + // This will only be called when the plugin has died unexpectedly + virtual void pluginDied() {}; +}; + +class LLPluginProcessParent : public LLPluginMessagePipeOwner +{ + LOG_CLASS(LLPluginProcessParent); +public: + LLPluginProcessParent(LLPluginProcessParentOwner *owner); + ~LLPluginProcessParent(); + + void init(const std::string &launcher_filename, const std::string &plugin_filename); + void idle(void); + + // returns true if the plugin is on its way to steady state + bool isLoading(void); + + // returns true if the plugin is in the steady state (processing messages) + bool isRunning(void); + + // returns true if the process has exited or we've had a fatal error + bool isDone(void); + + void killSockets(void); + + void setSleepTime(F64 sleep_time, bool force_send = false); + F64 getSleepTime(void) const { return mSleepTime; }; + + void sendMessage(const LLPluginMessage &message); + + void receiveMessage(const LLPluginMessage &message); + + // Inherited from LLPluginMessagePipeOwner + void receiveMessageRaw(const std::string &message); + + // This adds a memory segment shared with the client, generating a name for the segment. The name generated is guaranteed to be unique on the host. + // The caller must call removeSharedMemory first (and wait until getSharedMemorySize returns 0 for the indicated name) before re-adding a segment with the same name. + std::string addSharedMemory(size_t size); + // Negotiates for the removal of a shared memory segment. It is the caller's responsibility to ensure that nothing touches the memory + // after this has been called, since the segment will be unmapped shortly thereafter. + void removeSharedMemory(const std::string &name); + size_t getSharedMemorySize(const std::string &name); + void *getSharedMemoryAddress(const std::string &name); + + // Returns the version string the plugin indicated for the message class, or an empty string if that class wasn't in the list. + std::string getMessageClassVersion(const std::string &message_class); + + std::string getPluginVersion(void); + + bool getDisableTimeout() { return mDisableTimeout; }; + void setDisableTimeout(bool disable) { mDisableTimeout = disable; }; + +private: + + enum EState + { + STATE_UNINITIALIZED, + STATE_INITIALIZED, // init() has been called + STATE_LISTENING, // listening for incoming connection + STATE_LAUNCHED, // process has been launched + STATE_CONNECTED, // process has connected + STATE_HELLO, // first message from the plugin process has been received + STATE_LOADING, // process has been asked to load the plugin + STATE_RUNNING, // + STATE_ERROR, // generic bailout state + STATE_CLEANUP, // clean everything up + STATE_EXITING, // Tried to kill process, waiting for it to exit + STATE_DONE // + + }; + EState mState; + void setState(EState state); + + bool pluginLockedUp(); + bool pluginLockedUpOrQuit(); + + bool accept(); + + LLSocket::ptr_t mListenSocket; + LLSocket::ptr_t mSocket; + U32 mBoundPort; + + LLProcessLauncher mProcess; + + std::string mPluginFile; + + LLPluginProcessParentOwner *mOwner; + + typedef std::map<std::string, LLPluginSharedMemory*> sharedMemoryRegionsType; + sharedMemoryRegionsType mSharedMemoryRegions; + + LLSD mMessageClassVersions; + std::string mPluginVersionString; + + LLTimer mHeartbeat; + F64 mSleepTime; + + bool mDisableTimeout; +}; + +#endif // LL_LLPLUGINPROCESSPARENT_H diff --git a/indra/llplugin/llpluginsharedmemory.cpp b/indra/llplugin/llpluginsharedmemory.cpp new file mode 100644 index 0000000000..ce8b8e3e09 --- /dev/null +++ b/indra/llplugin/llpluginsharedmemory.cpp @@ -0,0 +1,495 @@ +/** + * @file llpluginsharedmemory.cpp + * @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llpluginsharedmemory.h" + +// on Mac and Linux, we use the native shm_open/mmap interface by using +// #define USE_SHM_OPEN_SHARED_MEMORY 1 +// in the appropriate sections below. + +// For Windows, use: +// #define USE_WIN32_SHARED_MEMORY 1 + +// If we ever want to fall back to the apr implementation for a platform, use: +// #define USE_APR_SHARED_MEMORY 1 + +#if LL_WINDOWS +// #define USE_APR_SHARED_MEMORY 1 + #define USE_WIN32_SHARED_MEMORY 1 +#elif LL_DARWIN + #define USE_SHM_OPEN_SHARED_MEMORY 1 +#elif LL_LINUX + #define USE_SHM_OPEN_SHARED_MEMORY 1 +#endif + + +// FIXME: This path thing is evil and unacceptable. +#if LL_WINDOWS + #define APR_SHARED_MEMORY_PREFIX_STRING "C:\\LLPlugin_" + // Apparnently using the "Global\\" prefix here only works from administrative accounts under Vista. + // Other options I've seen referenced are "Local\\" and "Session\\". + #define WIN32_SHARED_MEMORY_PREFIX_STRING "Local\\LL_" +#else + // mac and linux + #define APR_SHARED_MEMORY_PREFIX_STRING "/tmp/LLPlugin_" + #define SHM_OPEN_SHARED_MEMORY_PREFIX_STRING "/LL" +#endif + +#if USE_APR_SHARED_MEMORY + #include "llapr.h" + #include "apr_shm.h" +#elif USE_SHM_OPEN_SHARED_MEMORY + #include <sys/fcntl.h> + #include <sys/mman.h> + #include <errno.h> +#elif USE_WIN32_SHARED_MEMORY +#include <windows.h> +#endif // USE_APR_SHARED_MEMORY + + +int LLPluginSharedMemory::sSegmentNumber = 0; + +std::string LLPluginSharedMemory::createName(void) +{ + std::stringstream newname; + +#if LL_WINDOWS + newname << GetCurrentProcessId(); +#else // LL_WINDOWS + newname << getpid(); +#endif // LL_WINDOWS + + newname << "_" << sSegmentNumber++; + + return newname.str(); +} + +class LLPluginSharedMemoryPlatformImpl +{ +public: + LLPluginSharedMemoryPlatformImpl(); + ~LLPluginSharedMemoryPlatformImpl(); + +#if USE_APR_SHARED_MEMORY + apr_shm_t* mAprSharedMemory; +#elif USE_SHM_OPEN_SHARED_MEMORY + int mSharedMemoryFD; +#elif USE_WIN32_SHARED_MEMORY + HANDLE mMapFile; +#endif + +}; + +LLPluginSharedMemory::LLPluginSharedMemory() +{ + mSize = 0; + mMappedAddress = NULL; + mNeedsDestroy = false; + + mImpl = new LLPluginSharedMemoryPlatformImpl; +} + +LLPluginSharedMemory::~LLPluginSharedMemory() +{ + if(mNeedsDestroy) + destroy(); + else + detach(); + + unlink(); + + delete mImpl; +} + +#if USE_APR_SHARED_MEMORY +// MARK: apr implementation + +LLPluginSharedMemoryPlatformImpl::LLPluginSharedMemoryPlatformImpl() +{ + mAprSharedMemory = NULL; +} + +LLPluginSharedMemoryPlatformImpl::~LLPluginSharedMemoryPlatformImpl() +{ + +} + +bool LLPluginSharedMemory::map(void) +{ + mMappedAddress = apr_shm_baseaddr_get(mImpl->mAprSharedMemory); + if(mMappedAddress == NULL) + { + return false; + } + + return true; +} + +bool LLPluginSharedMemory::unmap(void) +{ + // This is a no-op under apr. + return true; +} + +bool LLPluginSharedMemory::close(void) +{ + // This is a no-op under apr. + return true; +} + +bool LLPluginSharedMemory::unlink(void) +{ + // This is a no-op under apr. + return true; +} + + +bool LLPluginSharedMemory::create(size_t size) +{ + mName = APR_SHARED_MEMORY_PREFIX_STRING; + mName += createName(); + mSize = size; + + apr_status_t status = apr_shm_create( &(mImpl->mAprSharedMemory), mSize, mName.c_str(), gAPRPoolp ); + + if(ll_apr_warn_status(status)) + { + return false; + } + + mNeedsDestroy = true; + + return map(); +} + +bool LLPluginSharedMemory::destroy(void) +{ + if(mImpl->mAprSharedMemory) + { + apr_status_t status = apr_shm_destroy(mImpl->mAprSharedMemory); + if(ll_apr_warn_status(status)) + { + // TODO: Is this a fatal error? I think not... + } + mImpl->mAprSharedMemory = NULL; + } + + return true; +} + +bool LLPluginSharedMemory::attach(const std::string &name, size_t size) +{ + mName = name; + mSize = size; + + apr_status_t status = apr_shm_attach( &(mImpl->mAprSharedMemory), mName.c_str(), gAPRPoolp ); + + if(ll_apr_warn_status(status)) + { + return false; + } + + return map(); +} + + +bool LLPluginSharedMemory::detach(void) +{ + if(mImpl->mAprSharedMemory) + { + apr_status_t status = apr_shm_detach(mImpl->mAprSharedMemory); + if(ll_apr_warn_status(status)) + { + // TODO: Is this a fatal error? I think not... + } + mImpl->mAprSharedMemory = NULL; + } + + return true; +} + + +#elif USE_SHM_OPEN_SHARED_MEMORY +// MARK: shm_open/mmap implementation + +LLPluginSharedMemoryPlatformImpl::LLPluginSharedMemoryPlatformImpl() +{ + mSharedMemoryFD = -1; +} + +LLPluginSharedMemoryPlatformImpl::~LLPluginSharedMemoryPlatformImpl() +{ +} + +bool LLPluginSharedMemory::map(void) +{ + mMappedAddress = ::mmap(NULL, mSize, PROT_READ | PROT_WRITE, MAP_SHARED, mImpl->mSharedMemoryFD, 0); + if(mMappedAddress == NULL) + { + return false; + } + + LL_DEBUGS("Plugin") << "memory mapped at " << mMappedAddress << LL_ENDL; + + return true; +} + +bool LLPluginSharedMemory::unmap(void) +{ + if(mMappedAddress != NULL) + { + LL_DEBUGS("Plugin") << "calling munmap(" << mMappedAddress << ", " << mSize << ")" << LL_ENDL; + if(::munmap(mMappedAddress, mSize) == -1) + { + // TODO: Is this a fatal error? I think not... + } + + mMappedAddress = NULL; + } + + return true; +} + +bool LLPluginSharedMemory::close(void) +{ + if(mImpl->mSharedMemoryFD != -1) + { + LL_DEBUGS("Plugin") << "calling close(" << mImpl->mSharedMemoryFD << ")" << LL_ENDL; + if(::close(mImpl->mSharedMemoryFD) == -1) + { + // TODO: Is this a fatal error? I think not... + } + + mImpl->mSharedMemoryFD = -1; + } + return true; +} + +bool LLPluginSharedMemory::unlink(void) +{ + if(!mName.empty()) + { + if(::shm_unlink(mName.c_str()) == -1) + { + return false; + } + } + + return true; +} + + +bool LLPluginSharedMemory::create(size_t size) +{ + mName = SHM_OPEN_SHARED_MEMORY_PREFIX_STRING; + mName += createName(); + mSize = size; + + // Preemptive unlink, just in case something didn't get cleaned up. + unlink(); + + mImpl->mSharedMemoryFD = ::shm_open(mName.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + if(mImpl->mSharedMemoryFD == -1) + { + return false; + } + + mNeedsDestroy = true; + + if(::ftruncate(mImpl->mSharedMemoryFD, mSize) == -1) + { + return false; + } + + + return map(); +} + +bool LLPluginSharedMemory::destroy(void) +{ + unmap(); + close(); + + return true; +} + + +bool LLPluginSharedMemory::attach(const std::string &name, size_t size) +{ + mName = name; + mSize = size; + + mImpl->mSharedMemoryFD = ::shm_open(mName.c_str(), O_RDWR, S_IRUSR | S_IWUSR); + if(mImpl->mSharedMemoryFD == -1) + { + return false; + } + + // unlink here so the segment will be cleaned up automatically after the last close. + unlink(); + + return map(); +} + +bool LLPluginSharedMemory::detach(void) +{ + unmap(); + close(); + return true; +} + +#elif USE_WIN32_SHARED_MEMORY +// MARK: Win32 CreateFileMapping-based implementation + +// Reference: http://msdn.microsoft.com/en-us/library/aa366551(VS.85).aspx + +LLPluginSharedMemoryPlatformImpl::LLPluginSharedMemoryPlatformImpl() +{ + mMapFile = NULL; +} + +LLPluginSharedMemoryPlatformImpl::~LLPluginSharedMemoryPlatformImpl() +{ + +} + +bool LLPluginSharedMemory::map(void) +{ + mMappedAddress = MapViewOfFile( + mImpl->mMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + mSize); + + if(mMappedAddress == NULL) + { + LL_WARNS("Plugin") << "MapViewOfFile failed: " << GetLastError() << LL_ENDL; + return false; + } + + LL_DEBUGS("Plugin") << "memory mapped at " << mMappedAddress << LL_ENDL; + + return true; +} + +bool LLPluginSharedMemory::unmap(void) +{ + if(mMappedAddress != NULL) + { + UnmapViewOfFile(mMappedAddress); + mMappedAddress = NULL; + } + + return true; +} + +bool LLPluginSharedMemory::close(void) +{ + if(mImpl->mMapFile != NULL) + { + CloseHandle(mImpl->mMapFile); + mImpl->mMapFile = NULL; + } + + return true; +} + +bool LLPluginSharedMemory::unlink(void) +{ + // This is a no-op on Windows. + return true; +} + + +bool LLPluginSharedMemory::create(size_t size) +{ + mName = WIN32_SHARED_MEMORY_PREFIX_STRING; + mName += createName(); + mSize = size; + + mImpl->mMapFile = CreateFileMappingA( + INVALID_HANDLE_VALUE, // use paging file + NULL, // default security + PAGE_READWRITE, // read/write access + 0, // max. object size + mSize, // buffer size + mName.c_str()); // name of mapping object + + if(mImpl->mMapFile == NULL) + { + LL_WARNS("Plugin") << "CreateFileMapping failed: " << GetLastError() << LL_ENDL; + return false; + } + + mNeedsDestroy = true; + + return map(); +} + +bool LLPluginSharedMemory::destroy(void) +{ + unmap(); + close(); + return true; +} + +bool LLPluginSharedMemory::attach(const std::string &name, size_t size) +{ + mName = name; + mSize = size; + + mImpl->mMapFile = OpenFileMappingA( + FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + mName.c_str()); // name of mapping object + + if(mImpl->mMapFile == NULL) + { + LL_WARNS("Plugin") << "OpenFileMapping failed: " << GetLastError() << LL_ENDL; + return false; + } + + return map(); +} + +bool LLPluginSharedMemory::detach(void) +{ + unmap(); + close(); + return true; +} + + + +#endif diff --git a/indra/llplugin/llpluginsharedmemory.h b/indra/llplugin/llpluginsharedmemory.h new file mode 100644 index 0000000000..a4613b9a54 --- /dev/null +++ b/indra/llplugin/llpluginsharedmemory.h @@ -0,0 +1,79 @@ +/** + * @file llpluginsharedmemory.h + * @brief LLPluginSharedMemory manages a shared memory segment for use by the LLPlugin API. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_LLPLUGINSHAREDMEMORY_H +#define LL_LLPLUGINSHAREDMEMORY_H + +class LLPluginSharedMemoryPlatformImpl; + +class LLPluginSharedMemory +{ + LOG_CLASS(LLPluginSharedMemory); +public: + LLPluginSharedMemory(); + ~LLPluginSharedMemory(); + + // Parent will use create/destroy, child will use attach/detach. + // Message transactions will ensure child attaches after parent creates and detaches before parent destroys. + + // create() implicitly creates a name for the segment which is guaranteed to be unique on the host at the current time. + bool create(size_t size); + bool destroy(void); + + bool attach(const std::string &name, size_t size); + bool detach(void); + + bool isMapped(void) const { return (mMappedAddress != NULL); }; + void *getMappedAddress(void) const { return mMappedAddress; }; + size_t getSize(void) const { return mSize; }; + std::string getName() const { return mName; }; + +private: + bool map(void); + bool unmap(void); + bool close(void); + bool unlink(void); + + std::string mName; + size_t mSize; + void *mMappedAddress; + bool mNeedsDestroy; + + LLPluginSharedMemoryPlatformImpl *mImpl; + + static int sSegmentNumber; + static std::string createName(); + +}; + + + +#endif // LL_LLPLUGINSHAREDMEMORY_H diff --git a/indra/llplugin/slplugin/CMakeLists.txt b/indra/llplugin/slplugin/CMakeLists.txt new file mode 100644 index 0000000000..4a7d670c23 --- /dev/null +++ b/indra/llplugin/slplugin/CMakeLists.txt @@ -0,0 +1,55 @@ +project(SLPlugin) + +include(00-Common) +include(LLCommon) +include(LLPlugin) +include(Linking) +include(PluginAPI) +include(LLMessage) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${LLMESSAGE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} +) + +if (DARWIN) + include(CMakeFindFrameworks) + find_library(CARBON_LIBRARY Carbon) +endif (DARWIN) + + +### SLPlugin + +set(SLPlugin_SOURCE_FILES + slplugin.cpp + ) + +add_executable(SLPlugin + WIN32 + ${SLPlugin_SOURCE_FILES} +) + +target_link_libraries(SLPlugin + ${LLPLUGIN_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +add_dependencies(SLPlugin + ${LLPLUGIN_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + +if (DARWIN) + # Mac version needs to link against carbon, and also needs an embedded plist (to set LSBackgroundOnly) + target_link_libraries(SLPlugin ${CARBON_LIBRARY}) + set_target_properties( + SLPlugin + PROPERTIES + LINK_FLAGS "-Wl,-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/slplugin_info.plist" + ) +endif (DARWIN) + diff --git a/indra/llplugin/slplugin/slplugin.cpp b/indra/llplugin/slplugin/slplugin.cpp new file mode 100644 index 0000000000..005e427572 --- /dev/null +++ b/indra/llplugin/slplugin/slplugin.cpp @@ -0,0 +1,243 @@ +/** + * @file slplugin.cpp + * @brief Loader shell for plugins, intended to be launched by the plugin host application, which directly loads a plugin dynamic library. + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llpluginprocesschild.h" +#include "llpluginmessage.h" +#include "llerrorcontrol.h" +#include "llapr.h" +#include "llstring.h" + +#if LL_DARWIN + #include <Carbon/Carbon.h> +#endif + +#if LL_DARWIN || LL_LINUX + #include <signal.h> +#endif + +/* + On Mac OS, since we call WaitNextEvent, this process will show up in the dock unless we set the LSBackgroundOnly flag in the Info.plist. + + Normally non-bundled binaries don't have an info.plist file, but it's possible to embed one in the binary by adding this to the linker flags: + + -sectcreate __TEXT __info_plist /path/to/slplugin_info.plist + + which means adding this to the gcc flags: + + -Wl,-sectcreate,__TEXT,__info_plist,/path/to/slplugin_info.plist + +*/ + +#if LL_DARWIN || LL_LINUX +// Signal handlers to make crashes not show an OS dialog... +static void crash_handler(int sig) +{ + // Just exit cleanly. + // TODO: add our own crash reporting + _exit(1); +} +#endif + +#if LL_WINDOWS +#include <windows.h> +//////////////////////////////////////////////////////////////////////////////// +// Our exception handler - will probably just exit and the host application +// will miss the heartbeat and log the error in the usual fashion. +LONG WINAPI myWin32ExceptionHandler( struct _EXCEPTION_POINTERS* exception_infop ) +{ + //std::cerr << "This plugin (" << __FILE__ << ") - "; + //std::cerr << "intercepted an unhandled exception and will exit immediately." << std::endl; + + // TODO: replace exception handler before we exit? + return EXCEPTION_EXECUTE_HANDLER; +} + +//////////////////////////////////////////////////////////////////////////////// +// Hook our exception handler and replace the system one +void initExceptionHandler() +{ + LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; + + // save old exception handler in case we need to restore it at the end + prev_filter = SetUnhandledExceptionFilter( myWin32ExceptionHandler ); +} + +bool checkExceptionHandler() +{ + bool ok = true; + LPTOP_LEVEL_EXCEPTION_FILTER prev_filter; + prev_filter = SetUnhandledExceptionFilter(myWin32ExceptionHandler); + + if (prev_filter != myWin32ExceptionHandler) + { + LL_WARNS("AppInit") << "Our exception handler (" << (void *)myWin32ExceptionHandler << ") replaced with " << prev_filter << "!" << LL_ENDL; + ok = false; + } + + if (prev_filter == NULL) + { + ok = FALSE; + if (myWin32ExceptionHandler == NULL) + { + LL_WARNS("AppInit") << "Exception handler uninitialized." << LL_ENDL; + } + else + { + LL_WARNS("AppInit") << "Our exception handler (" << (void *)myWin32ExceptionHandler << ") replaced with NULL!" << LL_ENDL; + } + } + + return ok; +} +#endif + +// If this application on Windows platform is a console application, a console is always +// created which is bad. Making it a Windows "application" via CMake settings but not +// adding any code to explicitly create windows does the right thing. +#if LL_WINDOWS +int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) +#else +int main(int argc, char **argv) +#endif +{ + ll_init_apr(); + + // Set up llerror logging + { + LLError::initForApplication("."); + LLError::setDefaultLevel(LLError::LEVEL_INFO); +// LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG); +// LLError::logToFile("slplugin.log"); + } + +#if LL_WINDOWS + if( strlen( lpCmdLine ) == 0 ) + { + LL_ERRS("slplugin") << "usage: " << "SLPlugin" << " launcher_port" << LL_ENDL; + }; + + U32 port = 0; + if(!LLStringUtil::convertToU32(lpCmdLine, port)) + { + LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL; + }; + + // Insert our exception handler into the system so this plugin doesn't + // display a crash message if something bad happens. The host app will + // see the missing heartbeat and log appropriately. + initExceptionHandler(); +#elif LL_DARWIN || LL_LINUX + if(argc < 2) + { + LL_ERRS("slplugin") << "usage: " << argv[0] << " launcher_port" << LL_ENDL; + } + + U32 port = 0; + if(!LLStringUtil::convertToU32(argv[1], port)) + { + LL_ERRS("slplugin") << "port number must be numeric" << LL_ENDL; + } + + // Catch signals that most kinds of crashes will generate, and exit cleanly so the system crash dialog isn't shown. + signal(SIGILL, &crash_handler); // illegal instruction +# if LL_DARWIN + signal(SIGEMT, &crash_handler); // emulate instruction executed +# endif // LL_DARWIN + signal(SIGFPE, &crash_handler); // floating-point exception + signal(SIGBUS, &crash_handler); // bus error + signal(SIGSEGV, &crash_handler); // segmentation violation + signal(SIGSYS, &crash_handler); // non-existent system call invoked +#endif + + LLPluginProcessChild *plugin = new LLPluginProcessChild(); + + plugin->init(port); + + LLTimer timer; + timer.start(); + +#if LL_WINDOWS + checkExceptionHandler(); +#endif + + while(!plugin->isDone()) + { + timer.reset(); + plugin->idle(); +#if LL_DARWIN + { + // Some plugins (webkit at least) will want an event loop. This qualifies. + EventRecord evt; + WaitNextEvent(0, &evt, 0, NULL); + } +#endif + F64 elapsed = timer.getElapsedTimeF64(); + F64 remaining = plugin->getSleepTime() - elapsed; + + if(remaining <= 0.0f) + { + // We've already used our full allotment. +// LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, not sleeping" << LL_ENDL; + + // Still need to service the network... + plugin->pump(); + } + else + { + +// LL_INFOS("slplugin") << "elapsed = " << elapsed * 1000.0f << " ms, remaining = " << remaining * 1000.0f << " ms, sleeping for " << remaining * 1000.0f << " ms" << LL_ENDL; +// timer.reset(); + + // This also services the network as needed. + plugin->sleep(remaining); + +// LL_INFOS("slplugin") << "slept for "<< timer.getElapsedTimeF64() * 1000.0f << " ms" << LL_ENDL; + } + +#if LL_WINDOWS + // More agressive checking of interfering exception handlers. + // Doesn't appear to be required so far - even for plugins + // that do crash with a single call to the intercept + // exception handler such as QuickTime. + //checkExceptionHandler(); +#endif + } + + delete plugin; + + ll_cleanup_apr(); + + return 0; +} + diff --git a/indra/llplugin/slplugin/slplugin_info.plist b/indra/llplugin/slplugin/slplugin_info.plist new file mode 100644 index 0000000000..b1daf87424 --- /dev/null +++ b/indra/llplugin/slplugin/slplugin_info.plist @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>LSBackgroundOnly</key> + <true/> +</dict> +</plist> diff --git a/indra/llrender/llfontfreetype.cpp b/indra/llrender/llfontfreetype.cpp index 62534969b7..0be6bedbee 100644 --- a/indra/llrender/llfontfreetype.cpp +++ b/indra/llrender/llfontfreetype.cpp @@ -33,6 +33,7 @@ #include "linden_common.h" #include "llfontfreetype.h" +#include "llfontgl.h" // Freetype stuff #include <ft2build.h> @@ -204,6 +205,19 @@ BOOL LLFontFreetype::loadFace(const std::string& filename, F32 point_size, F32 v mName = filename; mPointSize = point_size; + mStyle = LLFontGL::NORMAL; + if(mFTFace->style_flags & FT_STYLE_FLAG_BOLD) + { + mStyle |= LLFontGL::BOLD; + mStyle &= ~LLFontGL::NORMAL; + } + + if(mFTFace->style_flags & FT_STYLE_FLAG_ITALIC) + { + mStyle |= LLFontGL::ITALIC; + mStyle &= ~LLFontGL::NORMAL; + } + return TRUE; } diff --git a/indra/llrender/llfontgl.cpp b/indra/llrender/llfontgl.cpp index 32a008047c..d76b23248d 100644 --- a/indra/llrender/llfontgl.cpp +++ b/indra/llrender/llfontgl.cpp @@ -89,12 +89,10 @@ static F32 llfont_round_y(F32 y) LLFontGL::LLFontGL() { - clearEmbeddedChars(); } LLFontGL::~LLFontGL() { - clearEmbeddedChars(); } void LLFontGL::reset() @@ -117,8 +115,10 @@ BOOL LLFontGL::loadFace(const std::string& filename, F32 point_size, F32 vert_dp return mFontFreetype->loadFace(filename, point_size, vert_dpi, horz_dpi, components, is_fallback); } +static LLFastTimer::DeclareTimer FTM_RENDER_FONTS("Fonts"); + S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, - ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const + ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const { if(!sDisplayFont) //do not display texts { @@ -159,7 +159,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); - LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); + LLFastTimer t(FTM_RENDER_FONTS); gGL.color4fv( color.mV ); @@ -231,7 +231,7 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons BOOL draw_ellipses = FALSE; - if (use_ellipses && halign == LEFT) + if (use_ellipses) { // check for too long of a string if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) @@ -251,135 +251,69 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons { llwchar wch = wstr[i]; - // Handle embedded characters first, if they're enabled. - // Embedded characters are a hack for notecards - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) + if (!mFontFreetype->hasGlyph(wch)) { - LLImageGL* ext_image = ext_data->mImage; - const LLWString& label = ext_data->mLabel; - - F32 ext_height = (F32)ext_image->getHeight() * sScaleY; - - F32 ext_width = (F32)ext_image->getWidth() * sScaleX; - F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; - - if (!label.empty()) - { - ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; - } - - if (start_x + scaled_max_pixels < cur_x + ext_advance) - { - // Not enough room for this character. - break; - } - - if (last_bound_texture != ext_image) - { - gGL.getTexUnit(0)->bind(ext_image); - last_bound_texture = ext_image; - } - - // snap origin to whole screen pixel - const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX)); - const F32 ext_y = (F32)llround(cur_render_y + (EXT_Y_BEARING * sScaleY + mFontFreetype->getAscenderHeight() - mFontFreetype->getLineHeight())); - - LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); - LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); - drawGlyph(screen_rect, uv_rect, LLColor4::white, style_to_add, shadow, drop_shadow_strength); - - if (!label.empty()) - { - gGL.pushMatrix(); - //glLoadIdentity(); - //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); - //glScalef(sScaleX, sScaleY, 1.f); - getFontExtChar()->render(label, 0, - /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), - /*llfloor*/(cur_y / sScaleY), - color, - halign, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, - TRUE ); - gGL.popMatrix(); - } - - gGL.color4fv(color.mV); + addChar(wch); + } - chars_drawn++; - cur_x += ext_advance; - if (((i + 1) < length) && wstr[i+1]) - { - cur_x += EXT_KERNING * sScaleX; - } - cur_render_x = cur_x; + const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); + if (!fgi) + { + llerrs << "Missing Glyph Info" << llendl; + break; } - else + // Per-glyph bitmap texture. + LLImageGL *image_gl = mFontFreetype->getFontBitmapCache()->getImageGL(fgi->mBitmapNum); + if (last_bound_texture != image_gl) { - if (!mFontFreetype->hasGlyph(wch)) - { - addChar(wch); - } - - const LLFontGlyphInfo* fgi= mFontFreetype->getGlyphInfo(wch); - if (!fgi) - { - llerrs << "Missing Glyph Info" << llendl; - break; - } - // Per-glyph bitmap texture. - LLImageGL *image_gl = mFontFreetype->getFontBitmapCache()->getImageGL(fgi->mBitmapNum); - if (last_bound_texture != image_gl) - { - gGL.getTexUnit(0)->bind(image_gl); - last_bound_texture = image_gl; - } - - if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) - { - // Not enough room for this character. - break; - } - - // Draw the text at the appropriate location - //Specify vertices and texture coordinates - LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, - (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, - (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, - (fgi->mYBitmapOffset - PAD_UVY) * inv_height); - // snap glyph origin to whole screen pixel - LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), - llround(cur_render_y + (F32)fgi->mYBearing), - llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, - llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); - - drawGlyph(screen_rect, uv_rect, color, style_to_add, shadow, drop_shadow_strength); + gGL.getTexUnit(0)->bind(image_gl); + last_bound_texture = image_gl; + } - chars_drawn++; - cur_x += fgi->mXAdvance; - cur_y += fgi->mYAdvance; + if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) + { + // Not enough room for this character. + break; + } - llwchar next_char = wstr[i+1]; - if (next_char && (next_char < LAST_CHARACTER)) + // Draw the text at the appropriate location + //Specify vertices and texture coordinates + LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, + (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, + (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, + (fgi->mYBitmapOffset - PAD_UVY) * inv_height); + // snap glyph origin to whole screen pixel + LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), + llround(cur_render_y + (F32)fgi->mYBearing), + llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, + llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); + + drawGlyph(screen_rect, uv_rect, color, style_to_add, shadow, drop_shadow_strength); + + chars_drawn++; + cur_x += fgi->mXAdvance; + cur_y += fgi->mYAdvance; + + llwchar next_char = wstr[i+1]; + if (next_char && (next_char < LAST_CHARACTER)) + { + // Kern this puppy. + if (!mFontFreetype->hasGlyph(next_char)) { - // Kern this puppy. - if (!mFontFreetype->hasGlyph(next_char)) - { - addChar(next_char); - } - cur_x += mFontFreetype->getXKerning(wch, next_char); + addChar(next_char); } + cur_x += mFontFreetype->getXKerning(wch, next_char); + } - // Round after kerning. - // Must do this to cur_x, not just to cur_render_x, otherwise you - // will squish sub-pixel kerned characters too close together. - // For example, "CCCCC" looks bad. - cur_x = (F32)llfloor(cur_x + 0.5f); - //cur_y = (F32)llfloor(cur_y + 0.5f); + // Round after kerning. + // Must do this to cur_x, not just to cur_render_x, otherwise you + // will squish sub-pixel kerned characters too close together. + // For example, "CCCCC" looks bad. + cur_x = (F32)llfloor(cur_x + 0.5f); + //cur_y = (F32)llfloor(cur_y + 0.5f); - cur_render_x = cur_x; - cur_render_y = cur_y; - } + cur_render_x = cur_x; + cur_render_y = cur_y; } if (right_x) @@ -398,9 +332,9 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons gGL.end(); } - // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { + // recursively render ellipses at end of string // we've already reserved enough room gGL.pushMatrix(); @@ -427,12 +361,12 @@ S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, cons S32 LLFontGL::render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const { - return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE, FALSE); + return render(text, begin_offset, x, y, color, LEFT, BASELINE, NORMAL, NO_SHADOW, S32_MAX, S32_MAX, NULL, FALSE); } S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const { - return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, FALSE, use_ellipses); + return render(utf8str_to_wstring(text), begin_offset, x, y, color, halign, valign, style, shadow, max_chars, max_pixels, right_x, use_ellipses); } S32 LLFontGL::renderUTF8(const std::string &text, S32 begin_offset, S32 x, S32 y, const LLColor4 &color) const @@ -478,9 +412,9 @@ S32 LLFontGL::getWidth(const std::string& utf8text, S32 begin_offset, S32 max_ch return getWidth(wtext.c_str(), begin_offset, max_chars); } -S32 LLFontGL::getWidth(const llwchar* wchars, S32 begin_offset, S32 max_chars, BOOL use_embedded) const +S32 LLFontGL::getWidth(const llwchar* wchars, S32 begin_offset, S32 max_chars) const { - F32 width = getWidthF32(wchars, begin_offset, max_chars, use_embedded); + F32 width = getWidthF32(wchars, begin_offset, max_chars); return llround(width); } @@ -501,7 +435,7 @@ F32 LLFontGL::getWidthF32(const std::string& utf8text, S32 begin_offset, S32 max return getWidthF32(wtext.c_str(), begin_offset, max_chars); } -F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars, BOOL use_embedded) const +F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars) const { const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; @@ -509,34 +443,21 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars const S32 max_index = begin_offset + max_chars; for (S32 i = begin_offset; i < max_index; i++) { - const llwchar wch = wchars[i]; + llwchar wch = wchars[i]; if (wch == 0) { break; // done } - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) - { - // Handle crappy embedded hack - cur_x += getEmbeddedCharAdvance(ext_data); - if( ((i+1) < max_chars) && (i+1 < max_index)) - { - cur_x += EXT_KERNING * sScaleX; - } - } - else - { - cur_x += mFontFreetype->getXAdvance(wch); - llwchar next_char = wchars[i+1]; + cur_x += mFontFreetype->getXAdvance(wch); + llwchar next_char = wchars[i+1]; - if (((i + 1) < max_chars) - && next_char - && (next_char < LAST_CHARACTER)) - { - // Kern this puppy. - cur_x += mFontFreetype->getXKerning(wch, next_char); - } + if (((i + 1) < begin_offset + max_chars) + && next_char + && (next_char < LAST_CHARACTER)) + { + // Kern this puppy. + cur_x += mFontFreetype->getXKerning(wch, next_char); } // Round after kerning. cur_x = (F32)llfloor(cur_x + 0.5f); @@ -546,7 +467,7 @@ F32 LLFontGL::getWidthF32(const llwchar* wchars, S32 begin_offset, S32 max_chars } // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels -S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, BOOL end_on_word_boundary, BOOL use_embedded, F32* drawn_pixels) const +S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars, BOOL end_on_word_boundary) const { if (!wchars || !wchars[0] || max_chars == 0) { @@ -576,83 +497,51 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch break; } - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) + if (in_word) { - if (in_word) - { - in_word = FALSE; - } - else + if (iswspace(wch)) { - start_of_last_word = i; - } - cur_x += getEmbeddedCharAdvance(ext_data); - - if (scaled_max_pixels < cur_x) - { - clip = TRUE; - break; - } - - if (((i+1) < max_chars) && wchars[i+1]) - { - cur_x += EXT_KERNING * sScaleX; - } - - if( scaled_max_pixels < cur_x ) - { - clip = TRUE; - break; + if(wch !=(0x00A0)) + { + in_word = FALSE; + } } - } - else - { - if (in_word) + if (iswindividual(wch)) { - if (iswspace(wch)) + if (iswpunct(wchars[i+1])) { - if(wch !=(0x00A0)) - { - in_word = FALSE; - } + in_word=TRUE; } - if (iswindividual(wch)) + else { - if (iswpunct(wchars[i+1])) - { - in_word=TRUE; - } - else - { - in_word=FALSE; - start_of_last_word = i; - } + in_word=FALSE; + start_of_last_word = i; } } - else + } + else + { + start_of_last_word = i; + if (!iswspace(wch)||!iswindividual(wch)) { - start_of_last_word = i; - if (!iswspace(wch)||!iswindividual(wch)) - { - in_word = TRUE; - } + in_word = TRUE; } + } - cur_x += mFontFreetype->getXAdvance(wch); - - if (scaled_max_pixels < cur_x) - { - clip = TRUE; - break; - } + cur_x += mFontFreetype->getXAdvance(wch); + + if (scaled_max_pixels < cur_x) + { + clip = TRUE; + break; + } - if (((i+1) < max_chars) && wchars[i+1]) - { - // Kern this puppy. - cur_x += mFontFreetype->getXKerning(wch, wchars[i+1]); - } + if (((i+1) < max_chars) && wchars[i+1]) + { + // Kern this puppy. + cur_x += mFontFreetype->getXKerning(wch, wchars[i+1]); } + // Round after kerning. cur_x = (F32)llfloor(cur_x + 0.5f); drawn_x = cur_x; @@ -662,10 +551,6 @@ S32 LLFontGL::maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_ch { i = start_of_last_word; } - if (drawn_pixels) - { - *drawn_pixels = drawn_x; - } return i; } @@ -686,8 +571,7 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ { llwchar wch = wchars[i]; - const embedded_data_t* ext_data = getEmbeddedCharData(wch); - F32 char_width = ext_data ? getEmbeddedCharAdvance(ext_data) : mFontFreetype->getXAdvance(wch); + F32 char_width = mFontFreetype->getXAdvance(wch); if( scaled_max_pixels < (total_width + char_width) ) { @@ -705,7 +589,7 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ if ( i > 0 ) { // kerning - total_width += ext_data ? (EXT_KERNING * sScaleX) : mFontFreetype->getXKerning(wchars[i-1], wch); + total_width += mFontFreetype->getXKerning(wchars[i-1], wch); } // Round after kerning. @@ -715,7 +599,7 @@ S32 LLFontGL::firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_ return start_pos - drawable_chars; } -S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round, BOOL use_embedded) const +S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 target_x, F32 max_pixels, S32 max_chars, BOOL round) const { if (!wchars || !wchars[0] || max_chars == 0) { @@ -723,7 +607,6 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t } F32 cur_x = 0; - S32 pos = 0; target_x *= sScaleX; @@ -732,113 +615,50 @@ S32 LLFontGL::charFromPixelOffset(const llwchar* wchars, S32 begin_offset, F32 t F32 scaled_max_pixels = max_pixels * sScaleX; - for (S32 i = begin_offset; (i < max_index); i++) + S32 pos; + for (pos = begin_offset; pos < max_index; pos++) { - llwchar wch = wchars[i]; + llwchar wch = wchars[pos]; if (!wch) { break; // done } - const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; - if (ext_data) - { - F32 ext_advance = getEmbeddedCharAdvance(ext_data); - - if (round) - { - // Note: if the mouse is on the left half of the character, the pick is to the character's left - // If it's on the right half, the pick is to the right. - if (target_x < cur_x + ext_advance/2) - { - break; - } - } - else - { - if (target_x < cur_x + ext_advance) - { - break; - } - } + F32 char_width = mFontFreetype->getXAdvance(wch); - if (scaled_max_pixels < cur_x + ext_advance) + if (round) + { + // Note: if the mouse is on the left half of the character, the pick is to the character's left + // If it's on the right half, the pick is to the right. + if (target_x < cur_x + char_width*0.5f) { break; } - - pos++; - cur_x += ext_advance; - - if (((i + 1) < max_index) - && (wchars[(i + 1)])) - { - cur_x += EXT_KERNING * sScaleX; - } - // Round after kerning. - cur_x = (F32)llfloor(cur_x + 0.5f); } - else + else if (target_x < cur_x + char_width) { - F32 char_width = mFontFreetype->getXAdvance(wch); - - if (round) - { - // Note: if the mouse is on the left half of the character, the pick is to the character's left - // If it's on the right half, the pick is to the right. - if (target_x < cur_x + char_width*0.5f) - { - break; - } - } - else if (target_x < cur_x + char_width) - { - break; - } - - if (scaled_max_pixels < cur_x + char_width) - { - break; - } - - pos++; - cur_x += char_width; - - if (((i + 1) < max_index) - && (wchars[(i + 1)])) - { - llwchar next_char = wchars[i + 1]; - // Kern this puppy. - cur_x += mFontFreetype->getXKerning(wch, next_char); - } - - // Round after kerning. - cur_x = (F32)llfloor(cur_x + 0.5f); + break; } - } - return pos; -} + if (scaled_max_pixels < cur_x + char_width) + { + break; + } -void LLFontGL::addEmbeddedChar( llwchar wc, LLTexture* image, const std::string& label ) const -{ - LLWString wlabel = utf8str_to_wstring(label); - addEmbeddedChar(wc, image, wlabel); -} + cur_x += char_width; -void LLFontGL::addEmbeddedChar( llwchar wc, LLTexture* image, const LLWString& wlabel ) const -{ - embedded_data_t* ext_data = new embedded_data_t(image->getGLTexture(), wlabel); - mEmbeddedChars[wc] = ext_data; -} + if (((pos + 1) < max_index) + && (wchars[(pos + 1)])) + { + llwchar next_char = wchars[pos + 1]; + // Kern this puppy. + cur_x += mFontFreetype->getXKerning(wch, next_char); + } -void LLFontGL::removeEmbeddedChar(llwchar wc) const -{ - embedded_map_t::iterator iter = mEmbeddedChars.find(wc); - if (iter != mEmbeddedChars.end()) - { - delete iter->second; - mEmbeddedChars.erase(wc); + // Round after kerning. + cur_x = (F32)llfloor(cur_x + 0.5f); } + + return llmin(max_chars, pos - begin_offset); } BOOL LLFontGL::addChar(llwchar wch) const @@ -1154,38 +974,6 @@ LLFontGL &LLFontGL::operator=(const LLFontGL &source) return *this; } -const LLFontGL::embedded_data_t* LLFontGL::getEmbeddedCharData(llwchar wch) const -{ - // Handle crappy embedded hack - embedded_map_t::const_iterator iter = mEmbeddedChars.find(wch); - if (iter != mEmbeddedChars.end()) - { - return iter->second; - } - return NULL; -} - -F32 LLFontGL::getEmbeddedCharAdvance(const embedded_data_t* ext_data) const -{ - const LLWString& label = ext_data->mLabel; - LLImageGL* ext_image = ext_data->mImage; - - F32 ext_width = (F32)ext_image->getWidth(); - if( !label.empty() ) - { - ext_width += (EXT_X_BEARING + getFontExtChar()->getWidthF32(label.c_str())) * sScaleX; - } - - return (EXT_X_BEARING * sScaleX) + ext_width; -} - -void LLFontGL::clearEmbeddedChars() -{ - for_each(mEmbeddedChars.begin(), mEmbeddedChars.end(), DeletePairedPointer()); - mEmbeddedChars.clear(); -} - - void LLFontGL::renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const { gGL.texCoord2f(uv_rect.mRight, uv_rect.mTop); diff --git a/indra/llrender/llfontgl.h b/indra/llrender/llfontgl.h index 42ed7a381f..af8e0909af 100644 --- a/indra/llrender/llfontgl.h +++ b/indra/llrender/llfontgl.h @@ -97,7 +97,7 @@ public: BOOL loadFace(const std::string& filename, F32 point_size, const F32 vert_dpi, const F32 horz_dpi, const S32 components, BOOL is_fallback); S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign = LEFT, VAlign valign = BASELINE, U8 style = NORMAL, - ShadowType shadow = NO_SHADOW, S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, F32* right_x=NULL, BOOL use_embedded = FALSE, BOOL use_ellipses = FALSE) const; + ShadowType shadow = NO_SHADOW, S32 max_chars = S32_MAX, S32 max_pixels = S32_MAX, F32* right_x=NULL, BOOL use_ellipses = FALSE) const; S32 render(const LLWString &text, S32 begin_offset, F32 x, F32 y, const LLColor4 &color) const; // renderUTF8 does a conversion, so is slower! @@ -113,28 +113,24 @@ public: S32 getWidth(const std::string& utf8text) const; S32 getWidth(const llwchar* wchars) const; S32 getWidth(const std::string& utf8text, S32 offset, S32 max_chars ) const; - S32 getWidth(const llwchar* wchars, S32 offset, S32 max_chars, BOOL use_embedded = FALSE) const; + S32 getWidth(const llwchar* wchars, S32 offset, S32 max_chars) const; F32 getWidthF32(const std::string& utf8text) const; F32 getWidthF32(const llwchar* wchars) const; F32 getWidthF32(const std::string& text, S32 offset, S32 max_chars ) const; - F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars, BOOL use_embedded = FALSE ) const; + F32 getWidthF32(const llwchar* wchars, S32 offset, S32 max_chars) const; // The following are called often, frequently with large buffers, so do not use a string interface // Returns the max number of complete characters from text (up to max_chars) that can be drawn in max_pixels - S32 maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars = S32_MAX, BOOL end_on_word_boundary = FALSE, BOOL use_embedded = FALSE, F32* drawn_pixels = NULL) const; + S32 maxDrawableChars(const llwchar* wchars, F32 max_pixels, S32 max_chars = S32_MAX, BOOL end_on_word_boundary = FALSE) const; // Returns the index of the first complete characters from text that can be drawn in max_pixels // given that the character at start_pos should be the last character (or as close to last as possible). S32 firstDrawableChar(const llwchar* wchars, F32 max_pixels, S32 text_len, S32 start_pos=S32_MAX, S32 max_chars = S32_MAX) const; // Returns the index of the character closest to pixel position x (ignoring text to the right of max_pixels and max_chars) - S32 charFromPixelOffset(const llwchar* wchars, S32 char_offset, F32 x, F32 max_pixels=F32_MAX, S32 max_chars = S32_MAX, BOOL round = TRUE, BOOL use_embedded = FALSE) const; - - void addEmbeddedChar( llwchar wc, LLTexture* image, const std::string& label) const; - void addEmbeddedChar( llwchar wc, LLTexture* image, const LLWString& label) const; - void removeEmbeddedChar( llwchar wc ) const; + S32 charFromPixelOffset(const llwchar* wchars, S32 char_offset, F32 x, F32 max_pixels=F32_MAX, S32 max_chars = S32_MAX, BOOL round = TRUE) const; BOOL addChar(const llwchar wch) const; @@ -199,19 +195,6 @@ private: LLFontDescriptor mFontDescriptor; LLPointer<LLFontFreetype> mFontFreetype; - struct embedded_data_t - { - embedded_data_t(LLImageGL* image, const LLWString& label) : mImage(image), mLabel(label) {} - LLPointer<LLImageGL> mImage; - LLWString mLabel; - }; - - typedef std::map<llwchar,embedded_data_t*> embedded_map_t; - mutable embedded_map_t mEmbeddedChars; - - const embedded_data_t* getEmbeddedCharData(llwchar wch) const; - F32 getEmbeddedCharAdvance(const embedded_data_t* ext_data) const; - void clearEmbeddedChars(); void renderQuad(const LLRectf& screen_rect, const LLRectf& uv_rect, F32 slant_amt) const; void drawGlyph(const LLRectf& screen_rect, const LLRectf& uv_rect, const LLColor4& color, U8 style, ShadowType shadow, F32 drop_shadow_fade) const; diff --git a/indra/llrender/llfontregistry.cpp b/indra/llrender/llfontregistry.cpp index 553e7b8f9d..99f364a589 100644 --- a/indra/llrender/llfontregistry.cpp +++ b/indra/llrender/llfontregistry.cpp @@ -477,7 +477,6 @@ LLFontGL *LLFontRegistry::createFont(const LLFontDescriptor& desc) if (result) { - result->mFontFreetype->setStyle(match_desc->getStyle()); result->mFontDescriptor = desc; } else diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp index c3d1d9e894..9d2cd4867a 100644 --- a/indra/llrender/llimagegl.cpp +++ b/indra/llrender/llimagegl.cpp @@ -516,7 +516,6 @@ void LLImageGL::setImage(const LLImageRaw* imageraw) void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) { -// LLFastTimer t1(LLFastTimer::FTM_TEMP1); llpushcallstacks ; bool is_compressed = false; if (mFormatPrimary >= GL_COMPRESSED_RGBA_S3TC_DXT1_EXT && mFormatPrimary <= GL_COMPRESSED_RGBA_S3TC_DXT5_EXT) @@ -524,12 +523,10 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) is_compressed = true; } -// LLFastTimer t2(LLFastTimer::FTM_TEMP2); gGL.getTexUnit(0)->bind(this); if (mUseMipMaps) { -// LLFastTimer t2(LLFastTimer::FTM_TEMP3); if (data_hasmips) { // NOTE: data_in points to largest image; smaller images @@ -546,14 +543,13 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } if (is_compressed) { -// LLFastTimer t2(LLFastTimer::FTM_TEMP4); S32 tex_size = dataFormatBytes(mFormatPrimary, w, h); glCompressedTexImage2DARB(mTarget, gl_level, mFormatPrimary, w, h, 0, tex_size, (GLvoid *)data_in); stop_glerror(); } else { -// LLFastTimer t2(LLFastTimer::FTM_TEMP4); +// LLFastTimer t2(FTM_TEMP4); if(mFormatSwapBytes) { @@ -586,7 +582,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_GENERATE_MIPMAP_SGIS, TRUE); stop_glerror(); { -// LLFastTimer t2(LLFastTimer::FTM_TEMP4); +// LLFastTimer t2(FTM_TEMP4); if(mFormatSwapBytes) { @@ -646,7 +642,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) } llassert(w > 0 && h > 0 && cur_mip_data); { -// LLFastTimer t1(LLFastTimer::FTM_TEMP4); +// LLFastTimer t1(FTM_TEMP4); if(mFormatSwapBytes) { glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); @@ -732,7 +728,7 @@ void LLImageGL::setImage(const U8* data_in, BOOL data_hasmips) llpushcallstacks ; } -BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height) +BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update) { llpushcallstacks ; if (!width || !height) @@ -750,7 +746,8 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 return FALSE; } - if (x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height) + // HACK: allow the caller to explicitly force the fast path (i.e. using glTexSubImage2D here instead of calling setImage) even when updating the full texture. + if (!force_fast_update && x_pos == 0 && y_pos == 0 && width == getWidth() && height == getHeight() && data_width == width && data_height == height) { setImage(datap, FALSE); } @@ -827,9 +824,9 @@ BOOL LLImageGL::setSubImage(const U8* datap, S32 data_width, S32 data_height, S3 return TRUE; } -BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height) +BOOL LLImageGL::setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update) { - return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height); + return setSubImage(imageraw->getData(), imageraw->getWidth(), imageraw->getHeight(), x_pos, y_pos, width, height, force_fast_update); } // Copy sub image from frame buffer @@ -988,7 +985,6 @@ BOOL LLImageGL::createGLTexture(S32 discard_level, const U8* data_in, BOOL data_ LLImageGL::generateTextures(1, &mTexName); stop_glerror(); { -// LLFastTimer t1(LLFastTimer::FTM_TEMP6); llverify(gGL.getTexUnit(0)->bind(this)); glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(LLTexUnit::getInternalType(mBindTarget), GL_TEXTURE_MAX_LEVEL, mMaxDiscardLevel-discard_level); diff --git a/indra/llrender/llimagegl.h b/indra/llrender/llimagegl.h index 5f32b23356..987a1dc538 100644 --- a/indra/llrender/llimagegl.h +++ b/indra/llrender/llimagegl.h @@ -106,8 +106,8 @@ public: BOOL createGLTexture(S32 discard_level, const U8* data, BOOL data_hasmips = FALSE, S32 usename = 0); void setImage(const LLImageRaw* imageraw); void setImage(const U8* data_in, BOOL data_hasmips = FALSE); - BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height); - BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height); + BOOL setSubImage(const LLImageRaw* imageraw, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE); + BOOL setSubImage(const U8* datap, S32 data_width, S32 data_height, S32 x_pos, S32 y_pos, S32 width, S32 height, BOOL force_fast_update = FALSE); BOOL setSubImageFromFrameBuffer(S32 fb_x, S32 fb_y, S32 x_pos, S32 y_pos, S32 width, S32 height); BOOL setDiscardLevel(S32 discard_level); // Read back a raw image for this discard level, if it exists diff --git a/indra/llui/CMakeLists.txt b/indra/llui/CMakeLists.txt index bb1eb17806..753c1fe9e2 100644 --- a/indra/llui/CMakeLists.txt +++ b/indra/llui/CMakeLists.txt @@ -3,7 +3,6 @@ project(llui)
include(00-Common)
-include(LLAudio)
include(LLCommon)
include(LLImage)
include(LLMath)
@@ -12,9 +11,9 @@ include(LLRender) include(LLWindow)
include(LLVFS)
include(LLXML)
+include(LLXUIXML)
include_directories(
- ${LLAUDIO_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${LLIMAGE_INCLUDE_DIRS}
${LLMATH_INCLUDE_DIRS}
@@ -23,6 +22,7 @@ include_directories( ${LLWINDOW_INCLUDE_DIRS}
${LLVFS_INCLUDE_DIRS}
${LLXML_INCLUDE_DIRS}
+ ${LLXUIXML_INCLUDE_DIRS}
)
set(llui_SOURCE_FILES
@@ -45,7 +45,6 @@ set(llui_SOURCE_FILES llfocusmgr.cpp
llfunctorregistry.cpp
lliconctrl.cpp
- llinitparam.cpp
llkeywords.cpp
lllayoutstack.cpp
lllineeditor.cpp
@@ -84,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
@@ -124,7 +122,6 @@ set(llui_HEADER_FILES llhandle.h
llhtmlhelp.h
lliconctrl.h
- llinitparam.h
llkeywords.h
lllayoutstack.h
lllazyvalue.h
@@ -140,7 +137,6 @@ set(llui_HEADER_FILES llpanel.h
llprogressbar.h
llradiogroup.h
- llregistry.h
llresizebar.h
llresizehandle.h
llresmgr.h
@@ -165,8 +161,7 @@ set(llui_HEADER_FILES lltextbox.h
lltexteditor.h
lltextparser.h
- lltrans.h
- lluicolor.h
+ lltransutil.h
lluicolortable.h
lluiconstants.h
lluictrlfactory.h
@@ -195,6 +190,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/llbutton.cpp b/indra/llui/llbutton.cpp index c566282bef..98e8c9a988 100644 --- a/indra/llui/llbutton.cpp +++ b/indra/llui/llbutton.cpp @@ -419,8 +419,16 @@ BOOL LLButton::handleRightMouseDown(S32 x, S32 y, MASK mask) { setFocus(TRUE); } - } +// if (pointInView(x, y)) +// { +// } + } + // send the mouse down signal + LLUICtrl::handleRightMouseDown(x,y,mask); + // *TODO: Return result of LLUICtrl call above? Should defer to base class + // but this might change the mouse handling of existing buttons in a bad way + // if they are not mouse opaque. return TRUE; } @@ -432,15 +440,20 @@ BOOL LLButton::handleRightMouseUp(S32 x, S32 y, MASK mask) // Always release the mouse gFocusMgr.setMouseCapture( NULL ); - if (pointInView(x, y)) - { - mRightClickSignal(this, x,y,mask); - } +// if (pointInView(x, y)) +// { +// mRightMouseUpSignal(this, x,y,mask); +// } } else { childrenHandleRightMouseUp(x, y, mask); } + // send the mouse up signal + LLUICtrl::handleRightMouseUp(x,y,mask); + // *TODO: Return result of LLUICtrl call above? Should defer to base class + // but this might change the mouse handling of existing buttons in a bad way. + // if they are not mouse opaque. return TRUE; } @@ -788,7 +801,7 @@ void LLButton::draw() LLFontGL::NORMAL, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW, S32_MAX, text_width, - NULL, FALSE, mUseEllipses); + NULL, mUseEllipses); } LLUICtrl::draw(); @@ -924,6 +937,16 @@ void LLButton::setColor(const LLColor4& color) setImageColor(color); } +void LLButton::setAlpha(F32 alpha) +{ + LLColor4 temp = mImageColor.get(); + temp.setAlpha(alpha); + mImageColor.set(temp); + + temp = mDisabledImageColor.get(); + temp.setAlpha(alpha * 0.5f); + mDisabledImageColor.set(temp); +} void LLButton::setImageDisabled(LLPointer<LLUIImage> image) { diff --git a/indra/llui/llbutton.h b/indra/llui/llbutton.h index 249882013a..e51cd443fa 100644 --- a/indra/llui/llbutton.h +++ b/indra/llui/llbutton.h @@ -190,7 +190,8 @@ public: void setImageColor(const std::string& color_control); void setImageColor(const LLColor4& c); - virtual void setColor(const LLColor4& c); + /*virtual*/ void setColor(const LLColor4& c); + /*virtual*/ void setAlpha(F32 alpha); void setImages(const std::string &image_name, const std::string &selected_name); diff --git a/indra/llui/llcombobox.cpp b/indra/llui/llcombobox.cpp index 93d2b83c9f..ac56d15d1b 100644 --- a/indra/llui/llcombobox.cpp +++ b/indra/llui/llcombobox.cpp @@ -191,6 +191,7 @@ void LLComboBox::clear() mButton->setLabelSelected(LLStringUtil::null); mButton->setLabelUnselected(LLStringUtil::null); mList->deselectAllItems(); + mLastSelectedIndex = -1; } void LLComboBox::onCommit() @@ -296,6 +297,7 @@ BOOL LLComboBox::setSimple(const LLStringExplicit& name) if (found) { setLabel(name); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -312,6 +314,7 @@ void LLComboBox::setValue(const LLSD& value) { setLabel( mList->getSelectedItemLabel() ); } + mLastSelectedIndex = mList->getFirstSelectedIndex(); } } @@ -359,6 +362,7 @@ void LLComboBox::setLabel(const LLStringExplicit& name) if (mList->selectItemByLabel(name, FALSE)) { mTextEntry->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else { @@ -384,6 +388,7 @@ BOOL LLComboBox::remove(const std::string& name) { mList->deleteSingleItem(mList->getItemIndex(item)); } + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; @@ -436,6 +441,7 @@ BOOL LLComboBox::setCurrentByIndex( S32 index ) if (found) { setLabel(mList->getSelectedItemLabel()); + mLastSelectedIndex = index; } return found; } @@ -607,24 +613,24 @@ void LLComboBox::showList() mList->setVisible(TRUE); setUseBoundingRect(TRUE); - - mList->sortItems(); - mLastSelectedIndex = mList->getFirstSelectedIndex(); } void LLComboBox::hideList() { - // assert selection in list - mList->selectNthItem(mLastSelectedIndex); + if (mList->getVisible()) + { + // assert selection in list + mList->selectNthItem(mLastSelectedIndex); - mButton->setToggleState(FALSE); - mList->setVisible(FALSE); - mList->mouseOverHighlightNthItem(-1); + mButton->setToggleState(FALSE); + mList->setVisible(FALSE); + mList->mouseOverHighlightNthItem(-1); - setUseBoundingRect(FALSE); - if( gFocusMgr.getTopCtrl() == this ) - { - gFocusMgr.setTopCtrl(NULL); + setUseBoundingRect(FALSE); + if( gFocusMgr.getTopCtrl() == this ) + { + gFocusMgr.setTopCtrl(NULL); + } } } @@ -816,11 +822,13 @@ void LLComboBox::onTextEntry(LLLineEditor* line_editor) if (mList->selectItemByLabel(line_editor->getText(), FALSE)) { line_editor->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else { line_editor->setTentative(mTextEntryTentative); mList->deselectAllItems(); + mLastSelectedIndex = -1; } return; } @@ -887,6 +895,7 @@ void LLComboBox::updateSelection() if (mList->selectItemByLabel(full_string, FALSE)) { mTextEntry->setTentative(FALSE); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else if (mList->selectItemByPrefix(left_wstring, FALSE)) { @@ -897,6 +906,7 @@ void LLComboBox::updateSelection() mTextEntry->endSelection(); mTextEntry->setTentative(FALSE); mHasAutocompletedText = TRUE; + mLastSelectedIndex = mList->getFirstSelectedIndex(); } else // no matching items found { @@ -904,6 +914,7 @@ void LLComboBox::updateSelection() mTextEntry->setText(wstring_to_utf8str(user_wstring)); // removes text added by autocompletion mTextEntry->setTentative(mTextEntryTentative); mHasAutocompletedText = FALSE; + mLastSelectedIndex = -1; } } @@ -991,6 +1002,7 @@ BOOL LLComboBox::setCurrentByID(const LLUUID& id) if (found) { setLabel(mList->getSelectedItemLabel()); + mLastSelectedIndex = mList->getFirstSelectedIndex(); } return found; diff --git a/indra/llui/llcontainerview.cpp b/indra/llui/llcontainerview.cpp index 96948b659f..51ef80f4b9 100644 --- a/indra/llui/llcontainerview.cpp +++ b/indra/llui/llcontainerview.cpp @@ -42,7 +42,12 @@ #include "llscrollcontainer.h" #include "lluictrlfactory.h" -static LLDefaultChildRegistry::Register<LLContainerView> r("container_view"); +static LLDefaultChildRegistry::Register<LLContainerView> r1("container_view"); + +#include "llpanel.h" +#include "llstatview.h" +static ContainerViewRegistry::Register<LLStatView> r2("stat_view"); +static ContainerViewRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML); LLContainerView::LLContainerView(const LLContainerView::Params& p) : LLView(p), @@ -127,35 +132,31 @@ void LLContainerView::draw() void LLContainerView::reshape(S32 width, S32 height, BOOL called_from_parent) { - S32 desired_width = width; - S32 desired_height = height; + LLRect scroller_rect; + scroller_rect.setOriginAndSize(0, 0, width, height); if (mScrollContainer) { - BOOL dum_bool; - mScrollContainer->calcVisibleSize(&desired_width, &desired_height, &dum_bool, &dum_bool); + scroller_rect = mScrollContainer->getContentWindowRect(); } else { // if we're uncontained - make height as small as possible - desired_height = 0; + scroller_rect.mTop = 0; } - arrange(desired_width, desired_height, called_from_parent); + arrange(scroller_rect.getWidth(), scroller_rect.getHeight(), called_from_parent); // sometimes, after layout, our container will change size (scrollbars popping in and out) // if so, attempt another layout if (mScrollContainer) { - S32 new_container_width; - S32 new_container_height; - BOOL dum_bool; - mScrollContainer->calcVisibleSize(&new_container_width, &new_container_height, &dum_bool, &dum_bool); + LLRect new_container_rect = mScrollContainer->getContentWindowRect(); - if ((new_container_width != desired_width) || - (new_container_height != desired_height)) // the container size has changed, attempt to arrange again + if ((new_container_rect.getWidth() != scroller_rect.getWidth()) || + (new_container_rect.getHeight() != scroller_rect.getHeight())) // the container size has changed, attempt to arrange again { - arrange(new_container_width, new_container_height, called_from_parent); + arrange(new_container_rect.getWidth(), new_container_rect.getHeight(), called_from_parent); } } } diff --git a/indra/llui/llcontainerview.h b/indra/llui/llcontainerview.h index 9f3d1ac7ad..74e38e18bc 100644 --- a/indra/llui/llcontainerview.h +++ b/indra/llui/llcontainerview.h @@ -36,9 +36,13 @@ #include "stdtypes.h" #include "lltextbox.h" #include "llstatbar.h" +#include "llview.h" class LLScrollContainer; +struct ContainerViewRegistry : public LLChildRegistry<ContainerViewRegistry> +{}; + class LLContainerView : public LLView { public: @@ -55,6 +59,10 @@ public: mouse_opaque(false); } }; + + // my valid children are stored in this registry + typedef ContainerViewRegistry child_registry_t; + protected: LLContainerView(const Params& p); friend class LLUICtrlFactory; diff --git a/indra/llui/llfloater.cpp b/indra/llui/llfloater.cpp index d420d1141e..ca3829e1bd 100644 --- a/indra/llui/llfloater.cpp +++ b/indra/llui/llfloater.cpp @@ -711,10 +711,7 @@ void LLFloater::releaseFocus() gFocusMgr.setTopCtrl(NULL); } - if( gFocusMgr.childHasKeyboardFocus( this ) ) - { - gFocusMgr.setKeyboardFocus(NULL); - } + setFocus(FALSE); if( gFocusMgr.childHasMouseCapture( this ) ) { @@ -1074,7 +1071,7 @@ void LLFloater::setFocus( BOOL b ) } LLUICtrl* last_focus = gFocusMgr.getLastFocusForGroup(this); // a descendent already has focus - BOOL child_had_focus = gFocusMgr.childHasKeyboardFocus(this); + BOOL child_had_focus = hasFocus(); // give focus to first valid descendent LLPanel::setFocus(b); @@ -1598,9 +1595,9 @@ void LLFloater::draw() { if (hasFocus() && getDefaultButton()->getEnabled()) { - LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); + LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); // is this button a direct descendent and not a nested widget (e.g. checkbox)? - BOOL focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && focus_ctrl->getParent() == this; + BOOL focus_is_child_button = dynamic_cast<LLButton*>(focus_ctrl) != NULL && dynamic_cast<LLButton*>(focus_ctrl)->getParent() == this; // only enable default button when current focus is not a button getDefaultButton()->setBorderEnabled(!focus_is_child_button); } @@ -1620,7 +1617,7 @@ void LLFloater::draw() else { // draw children - LLView* focused_child = gFocusMgr.getKeyboardFocus(); + LLView* focused_child = dynamic_cast<LLView*>(gFocusMgr.getKeyboardFocus()); BOOL focused_child_visible = FALSE; if (focused_child && focused_child->getParent() == this) { @@ -1948,7 +1945,7 @@ LLRect LLFloaterView::findNeighboringPosition( LLFloater* reference_floater, LLF if (sibling && sibling != neighbor && sibling->getVisible() && - expanded_base_rect.rectInRect(&sibling->getRect())) + expanded_base_rect.overlaps(sibling->getRect())) { base_rect.unionWith(sibling->getRect()); } @@ -2593,6 +2590,8 @@ void LLFloater::initFromParams(const LLFloater::Params& p) initCommitCallback(p.close_callback, mCloseSignal); } +LLFastTimer::DeclareTimer POST_BUILD("Floater Post Build"); + void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node) { Params params(LLUICtrlFactory::getDefaultParams<LLFloater>()); @@ -2626,7 +2625,12 @@ void LLFloater::initFloaterXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr o LLFloater::setFloaterHost(last_host); } - BOOL result = postBuild(); + BOOL result; + { + LLFastTimer ft(POST_BUILD); + + result = postBuild(); + } if (!result) { diff --git a/indra/llui/llfocusmgr.cpp b/indra/llui/llfocusmgr.cpp index 3899897c5f..60ddbc6cb3 100644 --- a/indra/llui/llfocusmgr.cpp +++ b/indra/llui/llfocusmgr.cpp @@ -38,6 +38,77 @@ const F32 FOCUS_FADE_TIME = 0.3f; +// NOTE: the LLFocusableElement implementation has been moved here from lluictrl.cpp. + +LLFocusableElement::LLFocusableElement() +: mFocusLostCallback(NULL), + mFocusReceivedCallback(NULL), + mFocusChangedCallback(NULL), + mTopLostCallback(NULL), + mFocusCallbackUserData(NULL) +{ +} + +// virtual +BOOL LLFocusableElement::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + return FALSE; +} + +// virtual +BOOL LLFocusableElement::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +{ + return FALSE; +} + +// virtual +LLFocusableElement::~LLFocusableElement() +{ +} + +void LLFocusableElement::onFocusReceived() +{ + if( mFocusReceivedCallback ) + { + mFocusReceivedCallback( this, mFocusCallbackUserData ); + } + if( mFocusChangedCallback ) + { + mFocusChangedCallback( this, mFocusCallbackUserData ); + } +} + +void LLFocusableElement::onFocusLost() +{ + if( mFocusLostCallback ) + { + mFocusLostCallback( this, mFocusCallbackUserData ); + } + + if( mFocusChangedCallback ) + { + mFocusChangedCallback( this, mFocusCallbackUserData ); + } +} + +void LLFocusableElement::onTopLost() +{ + if (mTopLostCallback) + { + mTopLostCallback(this, mFocusCallbackUserData); + } +} + +BOOL LLFocusableElement::hasFocus() const +{ + return gFocusMgr.getKeyboardFocus() == this; +} + +void LLFocusableElement::setFocus(BOOL b) +{ +} + + LLFocusMgr gFocusMgr; LLFocusMgr::LLFocusMgr() @@ -86,11 +157,19 @@ void LLFocusMgr::releaseFocusIfNeeded( const LLView* view ) } -void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystrokes_only) +void LLFocusMgr::setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock, BOOL keystrokes_only) { + // notes if keyboard focus is changed again (by onFocusLost/onFocusReceived) + // making the rest of our processing unnecessary since it will already be + // handled by the recursive call + static bool focus_dirty; + focus_dirty = false; + if (mLockedView && (new_focus == NULL || - (new_focus != mLockedView && !new_focus->hasAncestor(mLockedView)))) + (new_focus != mLockedView + && dynamic_cast<LLView*>(new_focus) + && !dynamic_cast<LLView*>(new_focus)->hasAncestor(mLockedView)))) { // don't allow focus to go to anything that is not the locked focus // or one of its descendants @@ -104,51 +183,65 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke mLastKeyboardFocus = mKeyboardFocus; mKeyboardFocus = new_focus; + // list of the focus and it's ancestors + view_handle_list_t old_focus_list = mCachedKeyboardFocusList; view_handle_list_t new_focus_list; // walk up the tree to root and add all views to the new_focus_list - for (LLView* ctrl = mKeyboardFocus; ctrl && ctrl != LLUI::getRootView(); ctrl = ctrl->getParent()) + for (LLView* ctrl = dynamic_cast<LLView*>(mKeyboardFocus); ctrl && ctrl != LLUI::getRootView(); ctrl = ctrl->getParent()) { if (ctrl) { - new_focus_list.push_front(ctrl->getHandle()); + new_focus_list.push_back(ctrl->getHandle()); } } - view_handle_list_t::iterator new_focus_iter = new_focus_list.begin(); - view_handle_list_t::iterator old_focus_iter = mCachedKeyboardFocusList.begin(); - - // compare the new focus sub-tree to the old focus sub-tree - // iterate through the lists in lockstep until we get to a non-common ancestor - while ((new_focus_iter != new_focus_list.end()) && - (old_focus_iter != mCachedKeyboardFocusList.end()) && - ((*new_focus_iter) == (*old_focus_iter))) + // remove all common ancestors since their focus is unchanged + while (!new_focus_list.empty() && + !old_focus_list.empty() && + new_focus_list.back() == old_focus_list.back()) { - new_focus_iter++; - old_focus_iter++; + new_focus_list.pop_back(); + old_focus_list.pop_back(); } - - // call onFocusLost on all remaining in the old focus list - while (old_focus_iter != mCachedKeyboardFocusList.end()) - { - if (old_focus_iter->get() != NULL) { - old_focus_iter->get()->onFocusLost(); + + // walk up the old focus branch calling onFocusLost + // we bubble up the tree to release focus, and back down to add + for (view_handle_list_t::iterator old_focus_iter = old_focus_list.begin(); + old_focus_iter != old_focus_list.end() && !focus_dirty; + old_focus_iter++) + { + LLView* old_focus_view = old_focus_iter->get(); + if (old_focus_view) + { + mCachedKeyboardFocusList.pop_front(); + old_focus_view->onFocusLost(); } - old_focus_iter++; } - // call onFocusReceived on all remaining in the new focus list - while (new_focus_iter != new_focus_list.end()) + // walk down the new focus branch calling onFocusReceived + for (view_handle_list_t::reverse_iterator new_focus_riter = new_focus_list.rbegin(); + new_focus_riter != new_focus_list.rend() && !focus_dirty; + new_focus_riter++) + { + LLView* new_focus_view = new_focus_riter->get(); + if (new_focus_view) + { + mCachedKeyboardFocusList.push_front(new_focus_view->getHandle()); + new_focus_view->onFocusReceived(); + } + } + + // if focus was changed as part of an onFocusLost or onFocusReceived call + // stop iterating on current list since it is now invalid + if (focus_dirty) { - new_focus_iter->get()->onFocusReceived(); - new_focus_iter++; + return; } - // cache the new focus list for next time - swap(mCachedKeyboardFocusList, new_focus_list); - #ifdef _DEBUG - mKeyboardFocusName = new_focus ? new_focus->getName() : std::string("none"); + LLUICtrl* focus_ctrl = dynamic_cast<LLUICtrl*>(new_focus); + mKeyboardFocusName = focus_ctrl ? focus_ctrl->getName() : std::string("none"); #endif // If we've got a default keyboard focus, and the caller is @@ -158,8 +251,8 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke mDefaultKeyboardFocus->setFocus(TRUE); } - LLView* focus_subtree = mKeyboardFocus; - LLView* viewp = mKeyboardFocus; + LLView* focus_subtree = dynamic_cast<LLView*>(mKeyboardFocus); + LLView* viewp = dynamic_cast<LLView*>(mKeyboardFocus); // find root-most focus root while(viewp) { @@ -173,7 +266,8 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke if (focus_subtree) { - mFocusHistory[focus_subtree->getHandle()] = mKeyboardFocus ? mKeyboardFocus->getHandle() : LLHandle<LLView>(); + LLView* focused_view = dynamic_cast<LLView*>(mKeyboardFocus); + mFocusHistory[focus_subtree->getHandle()] = focused_view ? focused_view->getHandle() : LLHandle<LLView>(); } } @@ -181,13 +275,15 @@ void LLFocusMgr::setKeyboardFocus(LLUICtrl* new_focus, BOOL lock, BOOL keystroke { lockFocus(); } + + focus_dirty = true; } // Returns TRUE is parent or any descedent of parent has keyboard focus. BOOL LLFocusMgr::childHasKeyboardFocus(const LLView* parent ) const { - LLView* focus_view = mKeyboardFocus; + LLView* focus_view = dynamic_cast<LLView*>(mKeyboardFocus); while( focus_view ) { if( focus_view == parent ) @@ -217,7 +313,7 @@ BOOL LLFocusMgr::childHasMouseCapture( const LLView* parent ) const return FALSE; } -void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLView* focus ) +void LLFocusMgr::removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ) { // should be ok to unlock here, as you have to know the locked view // in order to unlock it @@ -335,7 +431,7 @@ void LLFocusMgr::removeTopCtrlWithoutCallback( const LLUICtrl* top_view ) void LLFocusMgr::lockFocus() { - mLockedView = mKeyboardFocus; + mLockedView = dynamic_cast<LLUICtrl*>(mKeyboardFocus); } void LLFocusMgr::unlockFocus() diff --git a/indra/llui/llfocusmgr.h b/indra/llui/llfocusmgr.h index fbe0d22084..d0adadd6d3 100644 --- a/indra/llui/llfocusmgr.h +++ b/indra/llui/llfocusmgr.h @@ -37,10 +37,44 @@ #include "llstring.h" #include "llframetimer.h" -#include "llview.h" +#include "llui.h" class LLUICtrl; class LLMouseHandler; +class LLView; + +// NOTE: the LLFocusableElement class declaration has been moved here from lluictrl.h. +class LLFocusableElement +{ + friend class LLFocusMgr; // allow access to focus change handlers +public: + LLFocusableElement(); + virtual ~LLFocusableElement(); + + virtual void setFocus( BOOL b ); + virtual BOOL hasFocus() const; + + typedef boost::function<void(LLFocusableElement*, void*)> focus_callback_t; + void setFocusLostCallback(focus_callback_t cb, void* user_data = NULL) { mFocusLostCallback = cb; mFocusCallbackUserData = user_data; } + void setFocusReceivedCallback(focus_callback_t cb, void* user_data = NULL) { mFocusReceivedCallback = cb; mFocusCallbackUserData = user_data; } + void setFocusChangedCallback(focus_callback_t cb, void* user_data = NULL ) { mFocusChangedCallback = cb; mFocusCallbackUserData = user_data; } + void setTopLostCallback(focus_callback_t cb, void* user_data = NULL ) { mTopLostCallback = cb; mFocusCallbackUserData = user_data; } + + // These were brought up the hierarchy from LLView so that we don't have to use dynamic_cast when dealing with keyboard focus. + virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); + virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + +protected: + virtual void onFocusReceived(); + virtual void onFocusLost(); + virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere + focus_callback_t mFocusLostCallback; + focus_callback_t mFocusReceivedCallback; + focus_callback_t mFocusChangedCallback; + focus_callback_t mTopLostCallback; + void* mFocusCallbackUserData; +}; + class LLFocusMgr { @@ -55,11 +89,11 @@ public: BOOL childHasMouseCapture( const LLView* parent ) const; // Keyboard Focus - void setKeyboardFocus(LLUICtrl* new_focus, BOOL lock = FALSE, BOOL keystrokes_only = FALSE); // new_focus = NULL to release the focus. - LLUICtrl* getKeyboardFocus() const { return mKeyboardFocus; } - LLUICtrl* getLastKeyboardFocus() const { return mLastKeyboardFocus; } + void setKeyboardFocus(LLFocusableElement* new_focus, BOOL lock = FALSE, BOOL keystrokes_only = FALSE); // new_focus = NULL to release the focus. + LLFocusableElement* getKeyboardFocus() const { return mKeyboardFocus; } + LLFocusableElement* getLastKeyboardFocus() const { return mLastKeyboardFocus; } BOOL childHasKeyboardFocus( const LLView* parent ) const; - void removeKeyboardFocusWithoutCallback( const LLView* focus ); + void removeKeyboardFocusWithoutCallback( const LLFocusableElement* focus ); BOOL getKeystrokesOnly() { return mKeystrokesOnly; } void setKeystrokesOnly(BOOL keystrokes_only) { mKeystrokesOnly = keystrokes_only; } @@ -74,8 +108,8 @@ public: // If setKeyboardFocus(NULL) is called, and there is a non-NULL default // keyboard focus view, focus goes there. JC - void setDefaultKeyboardFocus(LLUICtrl* default_focus) { mDefaultKeyboardFocus = default_focus; } - LLUICtrl* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; } + void setDefaultKeyboardFocus(LLFocusableElement* default_focus) { mDefaultKeyboardFocus = default_focus; } + LLFocusableElement* getDefaultKeyboardFocus() const { return mDefaultKeyboardFocus; } // Top View @@ -97,9 +131,9 @@ private: LLMouseHandler* mMouseCaptor; // Mouse events are premptively routed to this object // Keyboard Focus - LLUICtrl* mKeyboardFocus; // Keyboard events are preemptively routed to this object - LLUICtrl* mLastKeyboardFocus; // who last had focus - LLUICtrl* mDefaultKeyboardFocus; + LLFocusableElement* mKeyboardFocus; // Keyboard events are preemptively routed to this object + LLFocusableElement* mLastKeyboardFocus; // who last had focus + LLFocusableElement* mDefaultKeyboardFocus; BOOL mKeystrokesOnly; // caching list of keyboard focus ancestors for calling onFocusReceived and onFocusLost diff --git a/indra/llui/lliconctrl.cpp b/indra/llui/lliconctrl.cpp index 5c6ea663f3..673c742e7a 100644 --- a/indra/llui/lliconctrl.cpp +++ b/indra/llui/lliconctrl.cpp @@ -80,6 +80,14 @@ void LLIconCtrl::draw() LLUICtrl::draw(); } +// virtual +void LLIconCtrl::setAlpha(F32 alpha) +{ + LLColor4 temp = mColor.get(); + temp.setAlpha(alpha); + mColor.set(temp); +} + // virtual // value might be a string or a UUID void LLIconCtrl::setValue(const LLSD& value ) diff --git a/indra/llui/lliconctrl.h b/indra/llui/lliconctrl.h index ff25b0d53e..aceb70b9d5 100644 --- a/indra/llui/lliconctrl.h +++ b/indra/llui/lliconctrl.h @@ -71,6 +71,8 @@ public: std::string getImageName() const; + /*virtual*/ void setAlpha(F32 alpha); + void setColor(const LLColor4& color) { mColor = color; } private: diff --git a/indra/llui/llkeywords.cpp b/indra/llui/llkeywords.cpp index 30796a5ab9..db1611abb5 100644 --- a/indra/llui/llkeywords.cpp +++ b/indra/llui/llkeywords.cpp @@ -231,11 +231,13 @@ LLColor3 LLKeywords::readColor( const std::string& s ) return LLColor3( r, g, b ); } +LLFastTimer::DeclareTimer FTM_SYNTAX_COLORING("Syntax Coloring"); + // Walk through a string, applying the rules specified by the keyword token list and // create a list of color segments. -void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor) +void LLKeywords::findSegments(std::vector<LLTextSegmentPtr>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor, LLTextEditor& editor) { - std::for_each(seg_list->begin(), seg_list->end(), DeletePointer()); + LLFastTimer ft(FTM_SYNTAX_COLORING); seg_list->clear(); if( wtext.empty() ) @@ -245,7 +247,7 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS S32 text_len = wtext.size(); - seg_list->push_back( new LLTextSegment( LLColor3(defaultColor), 0, text_len ) ); + seg_list->push_back( new LLNormalTextSegment( defaultColor, 0, text_len, editor ) ); const llwchar* base = wtext.c_str(); const llwchar* cur = base; @@ -296,9 +298,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS } S32 seg_end = cur - base; - LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end ); + LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor ); text_segment->setToken( cur_token ); - insertSegment( seg_list, text_segment, text_len, defaultColor); + insertSegment( seg_list, text_segment, text_len, defaultColor, editor); line_done = TRUE; // to break out of second loop. break; } @@ -405,9 +407,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS } - LLTextSegment* text_segment = new LLTextSegment( cur_delimiter->getColor(), seg_start, seg_end ); + LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_delimiter->getColor(), seg_start, seg_end, editor ); text_segment->setToken( cur_delimiter ); - insertSegment( seg_list, text_segment, text_len, defaultColor); + insertSegment( seg_list, text_segment, text_len, defaultColor, editor); // Note: we don't increment cur, since the end of one delimited seg may be immediately // followed by the start of another one. @@ -438,9 +440,9 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS // llinfos << "Seg: [" << word.c_str() << "]" << llendl; - LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end ); + LLTextSegmentPtr text_segment = new LLNormalTextSegment( cur_token->getColor(), seg_start, seg_end, editor ); text_segment->setToken( cur_token ); - insertSegment( seg_list, text_segment, text_len, defaultColor); + insertSegment( seg_list, text_segment, text_len, defaultColor, editor); } cur += seg_len; continue; @@ -455,25 +457,24 @@ void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWS } } -void LLKeywords::insertSegment(std::vector<LLTextSegment*>* seg_list, LLTextSegment* new_segment, S32 text_len, const LLColor4 &defaultColor ) +void LLKeywords::insertSegment(std::vector<LLTextSegmentPtr>* seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, LLTextEditor& editor ) { - LLTextSegment* last = seg_list->back(); + LLTextSegmentPtr last = seg_list->back(); S32 new_seg_end = new_segment->getEnd(); if( new_segment->getStart() == last->getStart() ) { - *last = *new_segment; - delete new_segment; + seg_list->pop_back(); } else { last->setEnd( new_segment->getStart() ); - seg_list->push_back( new_segment ); } + seg_list->push_back( new_segment ); if( new_seg_end < text_len ) { - seg_list->push_back( new LLTextSegment( defaultColor, new_seg_end, text_len ) ); + seg_list->push_back( new LLNormalTextSegment( defaultColor, new_seg_end, text_len, editor ) ); } } diff --git a/indra/llui/llkeywords.h b/indra/llui/llkeywords.h index 38f5e993c2..53377869ca 100644 --- a/indra/llui/llkeywords.h +++ b/indra/llui/llkeywords.h @@ -39,9 +39,10 @@ #include <map> #include <list> #include <deque> +#include "llpointer.h" class LLTextSegment; - +typedef LLPointer<LLTextSegment> LLTextSegmentPtr; class LLKeywordToken { @@ -84,7 +85,7 @@ public: BOOL loadFromFile(const std::string& filename); BOOL isLoaded() const { return mLoaded; } - void findSegments(std::vector<LLTextSegment *> *seg_list, const LLWString& text, const LLColor4 &defaultColor ); + void findSegments(std::vector<LLTextSegmentPtr> *seg_list, const LLWString& text, const LLColor4 &defaultColor, class LLTextEditor& editor ); // Add the token as described void addToken(LLKeywordToken::TOKEN_TYPE type, @@ -103,7 +104,7 @@ public: private: LLColor3 readColor(const std::string& s); - void insertSegment(std::vector<LLTextSegment *> *seg_list, LLTextSegment* new_segment, S32 text_len, const LLColor4 &defaultColor); + void insertSegment(std::vector<LLTextSegmentPtr> *seg_list, LLTextSegmentPtr new_segment, S32 text_len, const LLColor4 &defaultColor, class LLTextEditor& editor); BOOL mLoaded; word_token_map_t mWordTokenMap; diff --git a/indra/llui/lllayoutstack.cpp b/indra/llui/lllayoutstack.cpp index 702d8e4a39..f98edec1f3 100644 --- a/indra/llui/lllayoutstack.cpp +++ b/indra/llui/lllayoutstack.cpp @@ -167,7 +167,7 @@ void LLLayoutStack::draw() LLLocalClipRect clip(clip_rect, mClip); // only force drawing invisible children if visible amount is non-zero - drawChild(panelp, 0, 0, !clip_rect.isNull()); + drawChild(panelp, 0, 0, !clip_rect.isEmpty()); } } diff --git a/indra/llui/lllineeditor.cpp b/indra/llui/lllineeditor.cpp index 20957499bc..5435b9ffbf 100644 --- a/indra/llui/lllineeditor.cpp +++ b/indra/llui/lllineeditor.cpp @@ -37,7 +37,6 @@ #include "lllineeditor.h" #include "lltexteditor.h" -#include "audioengine.h" #include "llmath.h" #include "llfontgl.h" #include "llgl.h" @@ -494,19 +493,19 @@ BOOL LLLineEditor::handleDoubleClick(S32 x, S32 y, MASK mask) BOOL doSelectAll = TRUE; // Select the word we're on - if( LLTextEditor::isPartOfWord( wtext[mCursorPos] ) ) + if( LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) { S32 old_selection_start = mLastSelectionStart; S32 old_selection_end = mLastSelectionEnd; // Select word the cursor is over - while ((mCursorPos > 0) && LLTextEditor::isPartOfWord( wtext[mCursorPos-1] )) + while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[mCursorPos-1] )) { // Find the start of the word mCursorPos--; } startSelection(); - while ((mCursorPos < (S32)wtext.length()) && LLTextEditor::isPartOfWord( wtext[mCursorPos] ) ) + while ((mCursorPos < (S32)wtext.length()) && LLWStringUtil::isPartOfWord( wtext[mCursorPos] ) ) { // Find the end of the word mCursorPos++; } @@ -624,7 +623,7 @@ BOOL LLLineEditor::handleMouseDown(S32 x, S32 y, MASK mask) // delay cursor flashing mKeystrokeTimer.reset(); - LLUICtrl::handleMouseDown(x,y,mask); + mMouseDownSignal(this,x,y,mask); return TRUE; } @@ -840,7 +839,7 @@ S32 LLLineEditor::prevWordPos(S32 cursorPos) const { cursorPos--; } - while( (cursorPos > 0) && LLTextEditor::isPartOfWord( wtext[cursorPos-1] ) ) + while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) { cursorPos--; } @@ -850,7 +849,7 @@ S32 LLLineEditor::prevWordPos(S32 cursorPos) const S32 LLLineEditor::nextWordPos(S32 cursorPos) const { const LLWString& wtext = mText.getWString(); - while( (cursorPos < getLength()) && LLTextEditor::isPartOfWord( wtext[cursorPos] ) ) + while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) { cursorPos++; } @@ -1732,11 +1731,11 @@ void LLLineEditor::draw() #endif // If we're editing... - if( gFocusMgr.getKeyboardFocus() == this) + if( hasFocus()) { //mBorder->setVisible(TRUE); // ok, programmer art just this once. // (Flash the cursor every half second) - if (gShowTextEditCursor && !mReadOnly) + if (!mReadOnly && gFocusMgr.getAppHasFocus()) { F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp index e5c32846a0..4edae46f32 100644 --- a/indra/llui/llmenugl.cpp +++ b/indra/llui/llmenugl.cpp @@ -184,6 +184,13 @@ LLMenuItemGL::LLMenuItemGL(const LLMenuItemGL::Params& p) << LL_ENDL; } +//virtual +void LLMenuItemGL::setValue(const LLSD& value) +{ + setLabel(value.asString()); +} + +//virtual BOOL LLMenuItemGL::handleAcceleratorKey(KEY key, MASK mask) { if( getEnabled() && (!gKeyboard->getKeyRepeated(key) || mAllowKeyRepeat) && (key == mAcceleratorKey) && (mask == (mAcceleratorMask & MASK_NORMALKEYS)) ) @@ -201,6 +208,26 @@ BOOL LLMenuItemGL::handleHover(S32 x, S32 y, MASK mask) return TRUE; } +//virtual +BOOL LLMenuItemGL::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + return LLUICtrl::handleRightMouseDown(x,y,mask); +} + +//virtual +BOOL LLMenuItemGL::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ + // If this event came from a right-click context menu spawn, + // process as a left-click to allow menu items to be hit + if (LLMenuHolderGL::sContextMenuSpawnPos.mX != S32_MAX + || LLMenuHolderGL::sContextMenuSpawnPos.mY != S32_MAX) + { + BOOL handled = handleMouseUp(x, y, mask); + return handled; + } + return LLUICtrl::handleRightMouseUp(x,y,mask); +} + // This function checks to see if the accelerator key is already in use; // if not, it will be added to the list BOOL LLMenuItemGL::addToAcceleratorList(std::list <LLKeyBinding*> *listp) @@ -306,6 +333,17 @@ U32 LLMenuItemGL::getNominalHeight( void ) const return llround(mFont->getLineHeight()) + MENU_ITEM_PADDING; } +//virtual +void LLMenuItemGL::setBriefItem(BOOL brief) +{ + mBriefItem = brief; +} + +//virtual +BOOL LLMenuItemGL::isBriefItem() const +{ + return mBriefItem; +} // Get the parent menu for this item LLMenuGL* LLMenuItemGL::getMenu() const @@ -800,15 +838,8 @@ BOOL LLMenuItemCallGL::handleAcceleratorKey( KEY key, MASK mask ) return FALSE; } -BOOL LLMenuItemCallGL::handleRightMouseUp(S32 x, S32 y, MASK mask) -{ - if (pointInView(x, y)) - { - mRightClickSignal(this,x,y, mask); - } - - return TRUE; -} +// handleRightMouseUp moved into base class LLMenuItemGL so clicks are +// handled for all menu item types ///============================================================================ /// Class LLMenuItemCheckGL @@ -898,24 +929,38 @@ LLMenuItemBranchGL::~LLMenuItemBranchGL() } // virtual -LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const +LLView* LLMenuItemBranchGL::getChildView(const std::string& name, BOOL recurse) const { LLMenuGL* branch = getBranch(); - if (!branch) - return LLView::getChildView(name, recurse, create_if_missing); - - // richard: this is redundant with parent, remove - if (branch->getName() == name) + if (branch) { - return branch; + if (branch->getName() == name) + { + return branch; + } + + // Always recurse on branches + return branch->getChildView(name, recurse); } - // Always recurse on branches - LLView* child = branch->getChildView(name, recurse, FALSE); - if (!child) + + return LLView::getChildView(name, recurse); +} + +LLView* LLMenuItemBranchGL::findChildView(const std::string& name, BOOL recurse) const +{ + LLMenuGL* branch = getBranch(); + if (branch) { - child = LLView::getChildView(name, recurse, create_if_missing); + if (branch->getName() == name) + { + return branch; + } + + // Always recurse on branches + return branch->findChildView(name, recurse); } - return child; + + return LLView::findChildView(name, recurse); } // virtual @@ -1123,6 +1168,18 @@ BOOL LLMenuItemBranchGL::handleKeyHere( KEY key, MASK mask ) return LLMenuItemGL::handleKeyHere(key, mask); } +//virtual +BOOL LLMenuItemBranchGL::isActive() const +{ + return isOpen() && getBranch() && getBranch()->getHighlightedItem(); +} + +//virtual +BOOL LLMenuItemBranchGL::isOpen() const +{ + return getBranch() && getBranch()->isOpen(); +} + void LLMenuItemBranchGL::openMenu() { LLMenuGL* branch = getBranch(); @@ -1296,20 +1353,26 @@ void LLMenuItemBranchDownGL::openMenu( void ) // set the hover status (called by it's menu) void LLMenuItemBranchDownGL::setHighlight( BOOL highlight ) { - if (highlight == getHighlight()) return; + if (highlight == getHighlight()) + return; //NOTE: Purposely calling all the way to the base to bypass auto-open. LLMenuItemGL::setHighlight(highlight); + + LLMenuGL* branch = getBranch(); + if (!branch) + return; + if( !highlight) { - if (getBranch()->getTornOff()) + if (branch->getTornOff()) { - ((LLFloater*)getBranch()->getParent())->setFocus(FALSE); - getBranch()->clearHoverItem(); + ((LLFloater*)branch->getParent())->setFocus(FALSE); + branch->clearHoverItem(); } else { - getBranch()->setVisible( FALSE ); + branch->setVisible( FALSE ); } } } @@ -1578,6 +1641,7 @@ LLMenuGL::LLMenuGL(const LLMenuGL::Params& p) mDropShadowed( p.drop_shadow ), mHorizontalLayout( p.horizontal_layout ), mScrollable(mHorizontalLayout ? FALSE : p.scrollable), // Scrolling is supported only for vertical layout + mMaxScrollableItems(p.max_scrollable_items), mKeepFixedSize( p.keep_fixed_size ), mLabel (p.label), mLastMouseX(0), @@ -1851,6 +1915,7 @@ void LLMenuGL::arrange( void ) item_list_t::iterator first_hidden_item_iter = mItems.end(); S32 height_before_first_visible_item = -1; S32 visible_items_height = 0; + U32 scrollable_items_cnt = 0; if (mHorizontalLayout) { @@ -1945,12 +2010,16 @@ void LLMenuGL::arrange( void ) { height_before_first_visible_item = height - (*item_iter)->getNominalHeight(); first_visible_item_iter = item_iter; + scrollable_items_cnt = 0; } - if (-1 != height_before_first_visible_item && 0 == visible_items_height && height - height_before_first_visible_item > max_height - spillover_item_height * 2) + if (-1 != height_before_first_visible_item && 0 == visible_items_height && + (++scrollable_items_cnt > mMaxScrollableItems || + height - height_before_first_visible_item > max_height - spillover_item_height * 2 )) { first_hidden_item_iter = item_iter; visible_items_height = height - height_before_first_visible_item - (*item_iter)->getNominalHeight(); + scrollable_items_cnt--; } } } @@ -1960,16 +2029,16 @@ void LLMenuGL::arrange( void ) { S32 max_items_height = max_height - spillover_item_height * 2; + if (visible_items_height == 0) + visible_items_height = height - height_before_first_visible_item; + // Fix mFirstVisibleItem value, if it doesn't allow to display all items, that can fit - if (visible_items_height < max_items_height) + if (visible_items_height < max_items_height && scrollable_items_cnt < mMaxScrollableItems) { - if (visible_items_height == 0) - { - visible_items_height = height - height_before_first_visible_item; - } - item_list_t::iterator tmp_iter(first_visible_item_iter); - while (visible_items_height < max_items_height && first_visible_item_iter != mItems.begin()) + while (visible_items_height < max_items_height && + scrollable_items_cnt < mMaxScrollableItems && + first_visible_item_iter != mItems.begin()) { if ((*first_visible_item_iter)->getVisible()) { @@ -1983,6 +2052,7 @@ void LLMenuGL::arrange( void ) { visible_items_height += (*first_visible_item_iter)->getNominalHeight(); height_before_first_visible_item -= (*first_visible_item_iter)->getNominalHeight(); + scrollable_items_cnt++; } } @@ -1991,6 +2061,7 @@ void LLMenuGL::arrange( void ) { visible_items_height -= (*first_visible_item_iter)->getNominalHeight(); height_before_first_visible_item += (*first_visible_item_iter)->getNominalHeight(); + scrollable_items_cnt--; first_visible_item_iter = tmp_iter; } if (!(*first_visible_item_iter)->getVisible()) @@ -2803,9 +2874,9 @@ void LLMenuGL::setVisible(BOOL visible) } } -LLMenuGL* LLMenuGL::getChildMenuByName(const std::string& name, BOOL recurse) const +LLMenuGL* LLMenuGL::findChildMenuByName(const std::string& name, BOOL recurse) const { - LLView* view = getChildView(name, recurse, FALSE); + LLView* view = findChildView(name, recurse); if (view) { LLMenuItemBranchGL* branch = dynamic_cast<LLMenuItemBranchGL*>(view); @@ -2844,9 +2915,19 @@ void hide_top_view( LLView* view ) } +// x and y are the desired location for the popup, NOT necessarily the +// mouse location // static void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) { + // Save click point for detecting cursor moves before mouse-up. + // Must be in local coords to compare with mouseUp events. + // If the mouse doesn't move, the menu will stay open ala the Mac. + // See also LLContextMenu::show() + S32 mouse_x, mouse_y; + LLUI::getCursorPositionLocal(menu->getParent(), &mouse_x, &mouse_y); + LLMenuHolderGL::sContextMenuSpawnPos.set(mouse_x,mouse_y); + const LLRect menu_region_rect = LLMenuGL::sMenuContainer->getMenuRect(); const S32 HPAD = 2; @@ -2857,9 +2938,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) spawning_view->localPointToOtherView(left, top, &left, &top, menu->getParent()); rect.setLeftTopAndSize( left, top, rect.getWidth(), rect.getHeight() ); - - - //rect.setLeftTopAndSize(x + HPAD, y, rect.getWidth(), rect.getHeight()); menu->setRect( rect ); // Resetting scrolling position @@ -2871,18 +2949,16 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) menu->arrangeAndClear(); // Fix menu rect if needed. rect = menu->getRect(); + // Adjust context menu to fit onscreen S32 bottom; left = rect.mLeft; bottom = rect.mBottom; - //menu->getParent()->localPointToScreen( rect.mLeft, rect.mBottom, - // &left, &bottom ); S32 delta_x = 0; S32 delta_y = 0; if( bottom < menu_region_rect.mBottom ) { // At this point, we need to move the context menu to the // other side of the mouse. - //delta_y = menu_region_rect.mBottom - bottom; delta_y = (rect.getHeight() + 2 * HPAD); } @@ -2890,7 +2966,6 @@ void LLMenuGL::showPopup(LLView* spawning_view, LLMenuGL* menu, S32 x, S32 y) { // At this point, we need to move the context menu to the // other side of the mouse. - //delta_x = (window_width - rect.getWidth()) - x; delta_x = -(rect.getWidth() + 2 * HPAD); } menu->translate( delta_x, delta_y ); @@ -3003,21 +3078,6 @@ BOOL LLMenuBarGL::handleMouseDown(S32 x, S32 y, MASK mask) return LLMenuGL::handleMouseDown(x, y, mask); } -/* -BOOL LLMenuBarGL::handleRightMouseDown(S32 x, S32 y, MASK mask) -{ - // clicks on menu bar closes existing menus from other contexts but leave - // own menu open so that we get toggle behavior - if (!getHighlightedItem() || !getHighlightedItem()->isActive()) - { - LLMenuGL::sMenuContainer->hideMenus(); - } - - return LLMenuGL::handleMouseDown(x, y, mask); -} -*/ - - void LLMenuBarGL::draw() { LLMenuItemGL* itemp = getHighlightedItem(); @@ -3400,17 +3460,14 @@ LLTearOffMenu::LLTearOffMenu(LLMenuGL* menup) : // highlight first item (tear off item will be disabled) mMenu->highlightNextItem(NULL); -} -LLTearOffMenu::~LLTearOffMenu() -{ + // Can't do this in postBuild() because that is only called for floaters + // constructed from XML. + mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this)); } -// virtual -BOOL LLTearOffMenu::postBuild() +LLTearOffMenu::~LLTearOffMenu() { - mCloseSignal.connect(boost::bind(&LLTearOffMenu::closeTearOff, this)); - return TRUE; } void LLTearOffMenu::draw() @@ -3651,6 +3708,7 @@ void LLContextMenu::show(S32 x, S32 y) // Save click point for detecting cursor moves before mouse-up. // Must be in local coords to compare with mouseUp events. // If the mouse doesn't move, the menu will stay open ala the Mac. + // See also LLMenuGL::showPopup() LLMenuHolderGL::sContextMenuSpawnPos.set(x,y); arrangeAndClear(); diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h index 930276f7bc..44459a6c0e 100644 --- a/indra/llui/llmenugl.h +++ b/indra/llui/llmenugl.h @@ -105,10 +105,15 @@ protected: LLMenuItemGL(const Params&); friend class LLUICtrlFactory; public: - virtual void setValue(const LLSD& value) { setLabel(value.asString()); } + // LLView overrides /*virtual*/ void handleVisibilityChange(BOOL new_visibility); + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); + + // LLUICtrl overrides + /*virtual*/ void setValue(const LLSD& value); - virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleAcceleratorKey(KEY key, MASK mask); LLColor4 getHighlightBgColor() { return mHighlightBackground.get(); } @@ -124,8 +129,8 @@ public: virtual U32 getNominalHeight( void ) const; // Marks item as not needing space for check marks or accelerator keys - virtual void setBriefItem(BOOL brief) { mBriefItem = brief; } - virtual BOOL isBriefItem() const { return mBriefItem; } + virtual void setBriefItem(BOOL brief); + virtual BOOL isBriefItem() const; virtual BOOL addToAcceleratorList(std::list<LLKeyBinding*> *listp); void setAllowKeyRepeat(BOOL allow) { mAllowKeyRepeat = allow; } @@ -283,7 +288,6 @@ public: virtual BOOL handleAcceleratorKey(KEY key, MASK mask); virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); //virtual void draw(); @@ -378,6 +382,7 @@ public: create_jump_keys, keep_fixed_size, scrollable; + Optional<U32> max_scrollable_items; Optional<LLUIColor> bg_color; Optional<S32> shortcut_pad; @@ -390,6 +395,7 @@ public: create_jump_keys("create_jump_keys", false), bg_color("bg_color", LLUIColorTable::instance().getColor( "MenuDefaultBgColor" )), scrollable("scrollable", false), + max_scrollable_items("max_scrollable_items", U32_MAX), shortcut_pad("shortcut_pad") { addSynonym(bg_visible, "opaque"); @@ -427,7 +433,7 @@ public: virtual BOOL handleAcceleratorKey(KEY key, MASK mask); - LLMenuGL* getChildMenuByName(const std::string& name, BOOL recurse) const; + LLMenuGL* findChildMenuByName(const std::string& name, BOOL recurse) const; BOOL clearHoverItem(); @@ -537,6 +543,7 @@ protected: S32 mLastMouseY; S32 mMouseVelX; S32 mMouseVelY; + U32 mMaxScrollableItems; BOOL mHorizontalLayout; BOOL mScrollable; BOOL mKeepFixedSize; @@ -610,9 +617,9 @@ public: virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual BOOL isActive() const { return isOpen() && getBranch() && getBranch()->getHighlightedItem(); } + virtual BOOL isActive() const; - virtual BOOL isOpen() const { return getBranch() && getBranch()->isOpen(); } + virtual BOOL isOpen() const; LLMenuGL* getBranch() const { return (LLMenuGL*)mBranchHandle.get(); } @@ -627,7 +634,8 @@ public: virtual void openMenu(); - virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const; + virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const; + virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const; private: LLHandle<LLView> mBranchHandle; @@ -709,9 +717,6 @@ public: /*virtual*/ BOOL handleKeyHere(KEY key, MASK mask); /*virtual*/ BOOL handleJumpKey(KEY key); /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); -// /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); - - /*virtual*/ void draw(); /*virtual*/ BOOL jumpKeysActive(); @@ -791,8 +796,6 @@ public: static LLTearOffMenu* create(LLMenuGL* menup); virtual ~LLTearOffMenu(); - virtual BOOL postBuild(); - virtual void draw(void); virtual void onFocusReceived(); virtual void onFocusLost(); diff --git a/indra/llui/llmodaldialog.cpp b/indra/llui/llmodaldialog.cpp index 11fa290de1..c8162fe466 100644 --- a/indra/llui/llmodaldialog.cpp +++ b/indra/llui/llmodaldialog.cpp @@ -279,10 +279,7 @@ void LLModalDialog::onAppFocusLost() gFocusMgr.setMouseCapture( NULL ); } - if( gFocusMgr.childHasKeyboardFocus( instance ) ) - { - gFocusMgr.setKeyboardFocus( NULL ); - } + instance->setFocus(FALSE); } } diff --git a/indra/llui/llmultifloater.cpp b/indra/llui/llmultifloater.cpp index 9f9e3aecac..e8ce1a8d97 100644 --- a/indra/llui/llmultifloater.cpp +++ b/indra/llui/llmultifloater.cpp @@ -454,14 +454,8 @@ BOOL LLMultiFloater::postBuild() return TRUE; } - requires<LLTabContainer>("Preview Tabs"); - if (checkRequirements()) - { - mTabContainer = getChild<LLTabContainer>("Preview Tabs"); - return TRUE; - } - - return FALSE; + mTabContainer = getChild<LLTabContainer>("Preview Tabs"); + return TRUE; } void LLMultiFloater::updateResizeLimits() diff --git a/indra/llui/llmultisliderctrl.cpp b/indra/llui/llmultisliderctrl.cpp index 1523d5d527..01a3b5fdc7 100644 --- a/indra/llui/llmultisliderctrl.cpp +++ b/indra/llui/llmultisliderctrl.cpp @@ -34,9 +34,6 @@ #include "llmultisliderctrl.h" -#include "audioengine.h" -#include "sound_ids.h" - #include "llmath.h" #include "llfontgl.h" #include "llgl.h" diff --git a/indra/llui/llpanel.cpp b/indra/llui/llpanel.cpp index 1a948fdd00..0b987bfcb5 100644 --- a/indra/llui/llpanel.cpp +++ b/indra/llui/llpanel.cpp @@ -94,7 +94,8 @@ LLPanel::LLPanel(const LLPanel::Params& p) mBorder(NULL), mLabel(p.label), mCommitCallbackRegistrar(false), - mEnableCallbackRegistrar(false) + mEnableCallbackRegistrar(false), + mXMLFilename("") { setIsChrome(FALSE); @@ -189,18 +190,19 @@ void LLPanel::draw() LLView::draw(); } -void LLPanel::updateDefaultBtn() +/*virtual*/ +void LLPanel::setAlpha(F32 alpha) { - // This method does not call LLView::draw() so callers will need - // to take care of that themselves at the appropriate place in - // their rendering sequence + mBgColorOpaque.setAlpha(alpha); +} +void LLPanel::updateDefaultBtn() +{ if( mDefaultBtn) { if (gFocusMgr.childHasKeyboardFocus( this ) && mDefaultBtn->getEnabled()) { - LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); - LLButton* buttonp = dynamic_cast<LLButton*>(focus_ctrl); + LLButton* buttonp = dynamic_cast<LLButton*>(gFocusMgr.getKeyboardFocus()); BOOL focus_is_child_button = buttonp && buttonp->getCommitOnReturn(); // only enable default button when current focus is not a return-capturing button mDefaultBtn->setBorderEnabled(!focus_is_child_button); @@ -248,12 +250,12 @@ BOOL LLPanel::handleKeyHere( KEY key, MASK mask ) { BOOL handled = FALSE; - LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus(); + LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus()); // handle user hitting ESC to defocus if (key == KEY_ESCAPE) { - gFocusMgr.setKeyboardFocus(NULL); + setFocus(FALSE); return TRUE; } else if( (mask == MASK_SHIFT) && (KEY_TAB == key)) @@ -314,53 +316,18 @@ void LLPanel::handleVisibilityChange ( BOOL new_visibility ) mVisibleSignal(this, LLSD(new_visibility) ); // Pass BOOL as LLSD } -BOOL LLPanel::checkRequirements() -{ - if (!mRequirementsError.empty()) - { - LLSD args; - args["COMPONENTS"] = mRequirementsError; - args["FLOATER"] = getName(); - - llwarns << getName() << " failed requirements check on: \n" - << mRequirementsError << llendl; - - LLNotifications::instance().add(LLNotification::Params("FailedRequirementsCheck").payload(args)); - mRequirementsError.clear(); - return FALSE; - } - - return TRUE; -} - void LLPanel::setFocus(BOOL b) { - if( b ) + if( b && !hasFocus()) { - if (!gFocusMgr.childHasKeyboardFocus(this)) - { - // give ourselves focus preemptively, to avoid infinite loop - LLUICtrl::setFocus(TRUE); - // then try to pass to first valid child - focusFirstItem(); - } + // give ourselves focus preemptively, to avoid infinite loop + LLUICtrl::setFocus(TRUE); + // then try to pass to first valid child + focusFirstItem(); } else { - if( this == gFocusMgr.getKeyboardFocus() ) - { - gFocusMgr.setKeyboardFocus( NULL ); - } - else - { - //RN: why is this here? - LLView::ctrl_list_t ctrls = getCtrlList(); - for (LLView::ctrl_list_t::iterator ctrl_it = ctrls.begin(); ctrl_it != ctrls.end(); ++ctrl_it) - { - LLUICtrl* ctrl = *ctrl_it; - ctrl->setFocus( FALSE ); - } - } + LLUICtrl::setFocus(b); } } @@ -486,8 +453,13 @@ BOOL LLPanel::initPanelXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr outpu LLFastTimer timer(FTM_PANEL_SETUP); LLXMLNodePtr referenced_xml; - std::string xml_filename; - node->getAttributeString("filename", xml_filename); + std::string xml_filename = mXMLFilename; + + // if the panel didn't provide a filename, check the node + if (xml_filename.empty()) + { + node->getAttributeString("filename", xml_filename); + } if (!xml_filename.empty()) { @@ -698,7 +670,6 @@ BOOL LLPanel::childHasFocus(const std::string& id) } else { - childNotFound(id); return FALSE; } } @@ -730,6 +701,14 @@ void LLPanel::childSetColor(const std::string& id, const LLColor4& color) child->setColor(color); } } +void LLPanel::childSetAlpha(const std::string& id, F32 alpha) +{ + LLUICtrl* child = getChild<LLUICtrl>(id, true); + if (child) + { + child->setAlpha(alpha); + } +} LLCtrlSelectionInterface* LLPanel::childGetSelectionInterface(const std::string& id) const { @@ -875,58 +854,3 @@ void LLPanel::childSetControlName(const std::string& id, const std::string& cont view->setControlName(control_name, NULL); } } - -//virtual -LLView* LLPanel::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const -{ - // just get child, don't try to create a dummy one - LLView* view = LLUICtrl::getChildView(name, recurse, FALSE); - if (!view && !recurse) - { - childNotFound(name); - } - if (!view && create_if_missing) - { - view = getDefaultWidget<LLView>(name); - if (!view) - { - // create LLViews explicitly, as they are not registered widget types - view = LLUICtrlFactory::createDefaultWidget<LLView>(name); - } - } - return view; -} - -void LLPanel::childNotFound(const std::string& id) const -{ - if (mExpectedMembers.find(id) == mExpectedMembers.end()) - { - mNewExpectedMembers.insert(id); - } -} - -void LLPanel::childDisplayNotFound() -{ - if (mNewExpectedMembers.empty()) - { - return; - } - std::string msg; - expected_members_list_t::iterator itor; - for (itor=mNewExpectedMembers.begin(); itor!=mNewExpectedMembers.end(); ++itor) - { - msg.append(*itor); - msg.append("\n"); - mExpectedMembers.insert(*itor); - } - mNewExpectedMembers.clear(); - LLSD args; - args["CONTROLS"] = msg; - LLNotifications::instance().add("FloaterNotFound", args); -} - -void LLPanel::requires(const std::string& name) -{ - requires<LLView>(name); -} - diff --git a/indra/llui/llpanel.h b/indra/llui/llpanel.h index 552a621a8e..28cd4d2799 100644 --- a/indra/llui/llpanel.h +++ b/indra/llui/llpanel.h @@ -115,11 +115,10 @@ public: /*virtual*/ BOOL handleKeyHere( KEY key, MASK mask ); /*virtual*/ void handleVisibilityChange ( BOOL new_visibility ); - // Override to set not found list: - /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const; - // From LLFocusableElement /*virtual*/ void setFocus( BOOL b ); + virtual void setAlpha(F32 alpha); + // New virtuals virtual void refresh(); // called in setFocus() @@ -132,19 +131,6 @@ public: BOOL hasBorder() const { return mBorder != NULL; } void setBorderVisible( BOOL b ); - template <class T> void requires(const std::string& name) - { - // check for widget with matching type and name - if (LLView::getChild<T>(name) == NULL) - { - mRequirementsError += name + "\n"; - } - } - - // requires LLView by default - void requires(const std::string& name); - BOOL checkRequirements(); - void setBackgroundColor( const LLColor4& color ) { mBgColorOpaque = color; } const LLColor4& getBackgroundColor() const { return mBgColorOpaque; } void setTransparentColor(const LLColor4& color) { mBgColorAlpha = color; } @@ -161,6 +147,7 @@ public: void setCtrlsEnabled(BOOL b); + LLHandle<LLPanel> getHandle() const { return mPanelHandle; } const LLCallbackMap::map_t& getFactoryMap() const { return mFactoryMap; } @@ -202,6 +189,7 @@ public: void childSetValidate(const std::string& id, boost::function<bool (const LLSD& data)> cb ); void childSetColor(const std::string& id, const LLColor4& color); + void childSetAlpha(const std::string& id, F32 alpha); LLCtrlSelectionInterface* childGetSelectionInterface(const std::string& id) const; LLCtrlListInterface* childGetListInterface(const std::string& id) const; @@ -241,14 +229,12 @@ public: void childSetControlName(const std::string& id, const std::string& control_name); - // Error reporting - void childNotFound(const std::string& id) const; - void childDisplayNotFound(); - static LLView* fromXML(LLXMLNodePtr node, LLView *parent, LLXMLNodePtr output_node = NULL); //call onOpen to let panel know when it's about to be shown or activated virtual void onOpen(const LLSD& key) {} + + void setXMLFilename(std::string filename) { mXMLFilename = filename; }; protected: // Override to set not found list @@ -257,7 +243,7 @@ protected: CommitCallbackRegistry::ScopedRegistrar mCommitCallbackRegistrar; EnableCallbackRegistry::ScopedRegistrar mEnableCallbackRegistrar; - commit_signal_t mVisibleSignal; // Called when visibilit changes, passes new visibility as LLSD() + commit_signal_t mVisibleSignal; // Called when visibility changes, passes new visibility as LLSD() private: // Unified error reporting for the child* functions @@ -277,7 +263,8 @@ private: typedef std::map<std::string, std::string> ui_string_map_t; ui_string_map_t mUIStrings; - std::string mRequirementsError; + // for setting the xml filename when building panel in context dependent cases + std::string mXMLFilename; }; // end class LLPanel diff --git a/indra/llui/llradiogroup.cpp b/indra/llui/llradiogroup.cpp index 30adbb023c..c66b9bde2b 100644 --- a/indra/llui/llradiogroup.cpp +++ b/indra/llui/llradiogroup.cpp @@ -90,18 +90,6 @@ BOOL LLRadioGroup::postBuild() return TRUE; } -// virtual -void LLRadioGroup::setEnabled(BOOL enabled) -{ - for (child_list_const_iter_t child_iter = getChildList()->begin(); - child_iter != getChildList()->end(); ++child_iter) - { - LLView *child = *child_iter; - child->setEnabled(enabled); - } - LLView::setEnabled(enabled); -} - void LLRadioGroup::setIndexEnabled(S32 index, BOOL enabled) { S32 count = 0; diff --git a/indra/llui/llradiogroup.h b/indra/llui/llradiogroup.h index d04473fa44..b5516307fd 100644 --- a/indra/llui/llradiogroup.h +++ b/indra/llui/llradiogroup.h @@ -106,9 +106,7 @@ public: virtual BOOL handleKeyHere(KEY key, MASK mask); - virtual void setEnabled(BOOL enabled); void setIndexEnabled(S32 index, BOOL enabled); - // return the index value of the selected item S32 getSelectedIndex() const { return mSelectedIndex; } diff --git a/indra/llui/llresizehandle.h b/indra/llui/llresizehandle.h index 1560a03796..1c09cbb1bd 100644 --- a/indra/llui/llresizehandle.h +++ b/indra/llui/llresizehandle.h @@ -76,8 +76,8 @@ private: const ECorner mCorner; }; -const S32 RESIZE_HANDLE_HEIGHT = 16; -const S32 RESIZE_HANDLE_WIDTH = 16; +const S32 RESIZE_HANDLE_HEIGHT = 11; +const S32 RESIZE_HANDLE_WIDTH = 11; #endif // LL_RESIZEHANDLE_H diff --git a/indra/llui/llscrollbar.cpp b/indra/llui/llscrollbar.cpp index 566825ff3b..b46915b379 100644 --- a/indra/llui/llscrollbar.cpp +++ b/indra/llui/llscrollbar.cpp @@ -394,7 +394,7 @@ BOOL LLScrollbar::handleHover(S32 x, S32 y, MASK mask) } else { - handled = childrenHandleMouseUp( x, y, mask ) != NULL; + handled = childrenHandleHover( x, y, mask ) != NULL; } // Opaque diff --git a/indra/llui/llscrollbar.h b/indra/llui/llscrollbar.h index 5522e5d0fa..30d906e04c 100644 --- a/indra/llui/llscrollbar.h +++ b/indra/llui/llscrollbar.h @@ -127,11 +127,6 @@ public: void onLineUpBtnPressed(const LLSD& data); void onLineDownBtnPressed(const LLSD& data); - void setTrackColor( const LLColor4& color ) { mTrackColor = color; } - void setThumbColor( const LLColor4& color ) { mThumbColor = color; } - - void setOnScrollEndCallback(void (*callback)(void*), void* userdata) { mOnScrollEndCallback = callback; mOnScrollEndData = userdata;} - private: void updateThumbRect(); void changeLine(S32 delta, BOOL update_thumb ); diff --git a/indra/llui/llscrollcontainer.cpp b/indra/llui/llscrollcontainer.cpp index 402c050d2e..13f862f3af 100644 --- a/indra/llui/llscrollcontainer.cpp +++ b/indra/llui/llscrollcontainer.cpp @@ -55,8 +55,6 @@ static const S32 HORIZONTAL_MULTIPLE = 8; static const S32 VERTICAL_MULTIPLE = 16; -static const F32 MIN_AUTO_SCROLL_RATE = 120.f; -static const F32 MAX_AUTO_SCROLL_RATE = 500.f; static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; ///---------------------------------------------------------------------------- @@ -65,9 +63,19 @@ static const F32 AUTO_SCROLL_RATE_ACCEL = 120.f; static LLDefaultChildRegistry::Register<LLScrollContainer> r("scroll_container"); +#include "llscrollingpanellist.h" +#include "llcontainerview.h" +#include "llpanel.h" +static ScrollContainerRegistry::Register<LLScrollingPanelList> r1("scrolling_panel_list"); +static ScrollContainerRegistry::Register<LLContainerView> r2("container_view"); +static ScrollContainerRegistry::Register<LLPanel> r3("panel", &LLPanel::fromXML); + LLScrollContainer::Params::Params() : is_opaque("opaque"), bg_color("color"), + border_visible("border_visible"), + min_auto_scroll_rate("min_auto_scroll_rate", 100), + max_auto_scroll_rate("max_auto_scroll_rate", 1000), reserve_scroll_corner("reserve_scroll_corner", false) { name = "scroll_container"; @@ -84,6 +92,8 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) mBackgroundColor(p.bg_color()), mIsOpaque(p.is_opaque), mReserveScrollCorner(p.reserve_scroll_corner), + mMinAutoScrollRate(p.min_auto_scroll_rate), + mMaxAutoScrollRate(p.max_auto_scroll_rate), mScrolledView(NULL) { static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); @@ -91,6 +101,7 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) LLViewBorder::Params params; params.name("scroll border"); params.rect(border_rect); + params.visible(p.border_visible); params.bevel_style(LLViewBorder::BEVEL_IN); mBorder = LLUICtrlFactory::create<LLViewBorder> (params); LLView::addChild( mBorder ); @@ -108,12 +119,11 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) sbparams.doc_pos(0); sbparams.page_size(mInnerRect.getHeight()); sbparams.step_size(VERTICAL_MULTIPLE); + sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); + sbparams.visible(false); + sbparams.change_callback(p.scroll_callback); mScrollbar[VERTICAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams); LLView::addChild( mScrollbar[VERTICAL] ); - mScrollbar[VERTICAL]->setVisible( FALSE ); - mScrollbar[VERTICAL]->setFollowsRight(); - mScrollbar[VERTICAL]->setFollowsTop(); - mScrollbar[VERTICAL]->setFollowsBottom(); LLRect horizontal_scroll_rect = mInnerRect; horizontal_scroll_rect.mTop = horizontal_scroll_rect.mBottom + scrollbar_size; @@ -124,11 +134,11 @@ LLScrollContainer::LLScrollContainer(const LLScrollContainer::Params& p) sbparams.doc_pos(0); sbparams.page_size(mInnerRect.getWidth()); sbparams.step_size(VERTICAL_MULTIPLE); + sbparams.visible(false); + sbparams.follows.flags(FOLLOWS_LEFT | FOLLOWS_RIGHT); + sbparams.change_callback(p.scroll_callback); mScrollbar[HORIZONTAL] = LLUICtrlFactory::create<LLScrollbar> (sbparams); LLView::addChild( mScrollbar[HORIZONTAL] ); - mScrollbar[HORIZONTAL]->setVisible( FALSE ); - mScrollbar[HORIZONTAL]->setFollowsLeft(); - mScrollbar[HORIZONTAL]->setFollowsRight(); } // Destroys the object @@ -174,7 +184,7 @@ void LLScrollContainer::reshape(S32 width, S32 height, { LLUICtrl::reshape( width, height, called_from_parent ); - mInnerRect.set( 0, getRect().getHeight(), getRect().getWidth(), 0 ); + mInnerRect = getLocalRect(); mInnerRect.stretch( -mBorder->getBorderWidth() ); if (mScrolledView) @@ -185,13 +195,14 @@ void LLScrollContainer::reshape(S32 width, S32 height, S32 visible_height = 0; BOOL show_v_scrollbar = FALSE; BOOL show_h_scrollbar = FALSE; - calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); mScrollbar[VERTICAL]->setPageSize( visible_height ); mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); mScrollbar[HORIZONTAL]->setPageSize( visible_width ); + updateScroll(); } } @@ -202,7 +213,7 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask) // NOTE: this should not recurse indefinitely as handleKeyHere // should not propagate to parent controls, so mScrolledView should *not* // call LLScrollContainer::handleKeyHere in turn - if (mScrolledView->handleKeyHere(key, mask)) + if (mScrolledView && mScrolledView->handleKeyHere(key, mask)) { return TRUE; } @@ -210,6 +221,7 @@ BOOL LLScrollContainer::handleKeyHere(KEY key, MASK mask) { if( mScrollbar[i]->handleKeyHere(key, mask) ) { + updateScroll(); return TRUE; } } @@ -226,6 +238,7 @@ BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) // Pretend the mouse is over the scrollbar if( mScrollbar[i]->handleScrollWheel( 0, 0, clicks ) ) { + updateScroll(); return TRUE; } } @@ -234,28 +247,6 @@ BOOL LLScrollContainer::handleScrollWheel( S32 x, S32 y, S32 clicks ) return TRUE; } -BOOL LLScrollContainer::needsToScroll(S32 x, S32 y, LLScrollContainer::SCROLL_ORIENTATION axis) const -{ - if(mScrollbar[axis]->getVisible()) - { - LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 ); - const S32 AUTOSCROLL_SIZE = 10; - if(mScrollbar[axis]->getVisible()) - { - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - inner_rect_local.mRight -= scrollbar_size; - inner_rect_local.mTop += AUTOSCROLL_SIZE; - inner_rect_local.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE; - } - if( inner_rect_local.pointInRect( x, y ) && (mScrollbar[axis]->getDocPos() > 0) ) - { - return TRUE; - } - - } - return FALSE; -} - BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, @@ -266,12 +257,27 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); // Scroll folder view if needed. Never accepts a drag or drop. *accept = ACCEPT_NO; - BOOL handled = FALSE; + BOOL handled = autoScroll(x, y); + + if( !handled ) + { + handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, + cargo_data, accept, tooltip_msg) != NULL; + } + + return TRUE; +} + +bool LLScrollContainer::autoScroll(S32 x, S32 y) +{ + static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); + + bool scrolling = false; if( mScrollbar[HORIZONTAL]->getVisible() || mScrollbar[VERTICAL]->getVisible() ) { - const S32 AUTOSCROLL_SIZE = 10; - S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); - + LLRect screen_local_extents; + screenRectToLocal(getRootView()->getLocalRect(), &screen_local_extents); + LLRect inner_rect_local( 0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0 ); if( mScrollbar[HORIZONTAL]->getVisible() ) { @@ -282,65 +288,61 @@ BOOL LLScrollContainer::handleDragAndDrop(S32 x, S32 y, MASK mask, inner_rect_local.mRight -= scrollbar_size; } + // clip rect against root view + inner_rect_local.intersectWith(screen_local_extents); + + S32 auto_scroll_speed = llround(mAutoScrollRate * LLFrameTimer::getFrameDeltaTimeF32()); + // autoscroll region should take up no more than one third of visible scroller area + S32 auto_scroll_region_width = llmin(inner_rect_local.getWidth() / 3, 10); + S32 auto_scroll_region_height = llmin(inner_rect_local.getHeight() / 3, 10); + if( mScrollbar[HORIZONTAL]->getVisible() ) { - LLRect left_scroll_rect = inner_rect_local; - left_scroll_rect.mRight = AUTOSCROLL_SIZE; + LLRect left_scroll_rect = screen_local_extents; + left_scroll_rect.mRight = inner_rect_local.mLeft + auto_scroll_region_width; if( left_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() > 0) ) { mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() - auto_scroll_speed ); mAutoScrolling = TRUE; - handled = TRUE; + scrolling = true; } - LLRect right_scroll_rect = inner_rect_local; - right_scroll_rect.mLeft = inner_rect_local.mRight - AUTOSCROLL_SIZE; + LLRect right_scroll_rect = screen_local_extents; + right_scroll_rect.mLeft = inner_rect_local.mRight - auto_scroll_region_width; if( right_scroll_rect.pointInRect( x, y ) && (mScrollbar[HORIZONTAL]->getDocPos() < mScrollbar[HORIZONTAL]->getDocPosMax()) ) { mScrollbar[HORIZONTAL]->setDocPos( mScrollbar[HORIZONTAL]->getDocPos() + auto_scroll_speed ); mAutoScrolling = TRUE; - handled = TRUE; + scrolling = true; } } if( mScrollbar[VERTICAL]->getVisible() ) { - LLRect bottom_scroll_rect = inner_rect_local; - bottom_scroll_rect.mTop = AUTOSCROLL_SIZE + bottom_scroll_rect.mBottom; + LLRect bottom_scroll_rect = screen_local_extents; + bottom_scroll_rect.mTop = inner_rect_local.mBottom + auto_scroll_region_height; if( bottom_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() < mScrollbar[VERTICAL]->getDocPosMax()) ) { mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() + auto_scroll_speed ); mAutoScrolling = TRUE; - handled = TRUE; + scrolling = true; } - LLRect top_scroll_rect = inner_rect_local; - top_scroll_rect.mBottom = inner_rect_local.mTop - AUTOSCROLL_SIZE; + LLRect top_scroll_rect = screen_local_extents; + top_scroll_rect.mBottom = inner_rect_local.mTop - auto_scroll_region_height; if( top_scroll_rect.pointInRect( x, y ) && (mScrollbar[VERTICAL]->getDocPos() > 0) ) { mScrollbar[VERTICAL]->setDocPos( mScrollbar[VERTICAL]->getDocPos() - auto_scroll_speed ); mAutoScrolling = TRUE; - handled = TRUE; + scrolling = true; } } } - - if( !handled ) - { - handled = childrenHandleDragAndDrop(x, y, mask, drop, cargo_type, - cargo_data, accept, tooltip_msg) != NULL; - } - - return TRUE; + return scrolling; } void LLScrollContainer::calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const { - const LLRect& rect = mScrolledView->getRect(); - calcVisibleSize(rect, visible_width, visible_height, show_h_scrollbar, show_v_scrollbar); -} - -void LLScrollContainer::calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const -{ + const LLRect& doc_rect = getScrolledViewRect(); static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); S32 doc_width = doc_rect.getWidth(); S32 doc_height = doc_rect.getHeight(); @@ -377,20 +379,20 @@ void LLScrollContainer::draw() if (mAutoScrolling) { // add acceleration to autoscroll - mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), MAX_AUTO_SCROLL_RATE); + mAutoScrollRate = llmin(mAutoScrollRate + (LLFrameTimer::getFrameDeltaTimeF32() * AUTO_SCROLL_RATE_ACCEL), mMaxAutoScrollRate); } else { - // reset to minimum - mAutoScrollRate = MIN_AUTO_SCROLL_RATE; + // reset to minimum for next time + mAutoScrollRate = mMinAutoScrollRate; } - // clear this flag to be set on next call to handleDragAndDrop + // clear this flag to be set on next call to autoScroll mAutoScrolling = FALSE; // auto-focus when scrollbar active // this allows us to capture user intent (i.e. stop automatically scrolling the view/etc) - if (!gFocusMgr.childHasKeyboardFocus(this) && - (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture())) + if (!hasFocus() + && (mScrollbar[VERTICAL]->hasMouseCapture() || mScrollbar[HORIZONTAL]->hasMouseCapture())) { focusFirstItem(); } @@ -417,7 +419,7 @@ void LLScrollContainer::draw() S32 visible_height = 0; BOOL show_v_scrollbar = FALSE; BOOL show_h_scrollbar = FALSE; - calcVisibleSize( mScrolledView->getRect(), &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); LLLocalClipRect clip(LLRect(mInnerRect.mLeft, mInnerRect.mBottom + (show_h_scrollbar ? scrollbar_size : 0) + visible_height, @@ -499,7 +501,7 @@ void LLScrollContainer::updateScroll() S32 visible_height = 0; BOOL show_v_scrollbar = FALSE; BOOL show_h_scrollbar = FALSE; - calcVisibleSize( doc_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); S32 border_width = mBorder->getBorderWidth(); if( show_v_scrollbar ) @@ -577,71 +579,73 @@ void LLScrollContainer::setBorderVisible(BOOL b) mBorder->setVisible( b ); } -// Scroll so that as much of rect as possible is showing (where rect is defined in the space of scroller view, not scrolled) -void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLCoordGL& desired_offset) +LLRect LLScrollContainer::getVisibleContentRect() { - if (!mScrolledView) - { - llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl; - return; - } + updateScroll(); + LLRect visible_rect = getContentWindowRect(); + LLRect contents_rect = mScrolledView->getRect(); + visible_rect.translate(-contents_rect.mLeft, -contents_rect.mBottom); + return visible_rect; +} +LLRect LLScrollContainer::getContentWindowRect() const +{ + LLRect scroller_view_rect; S32 visible_width = 0; S32 visible_height = 0; - BOOL show_v_scrollbar = FALSE; BOOL show_h_scrollbar = FALSE; - const LLRect& scrolled_rect = mScrolledView->getRect(); - calcVisibleSize( scrolled_rect, &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); - - // can't be so far left that right side of rect goes off screen, or so far right that left side does - S32 horiz_offset = llclamp(desired_offset.mX, llmin(0, -visible_width + rect.getWidth()), 0); - // can't be so high that bottom of rect goes off screen, or so low that top does - S32 vert_offset = llclamp(desired_offset.mY, 0, llmax(0, visible_height - rect.getHeight())); - - // Vertical - // 1. First make sure the top is visible - // 2. Then, if possible without hiding the top, make the bottom visible. - S32 vert_pos = mScrollbar[VERTICAL]->getDocPos(); - - // find scrollbar position to get top of rect on screen (scrolling up) - S32 top_offset = scrolled_rect.mTop - rect.mTop - vert_offset; - // find scrollbar position to get bottom of rect on screen (scrolling down) - S32 bottom_offset = vert_offset == 0 ? scrolled_rect.mTop - rect.mBottom - visible_height : top_offset; - // scroll up far enough to see top or scroll down just enough if item is bigger than visual area - if( vert_pos >= top_offset || visible_height < rect.getHeight()) - { - vert_pos = top_offset; - } - // else scroll down far enough to see bottom - else - if( vert_pos <= bottom_offset ) + BOOL show_v_scrollbar = FALSE; + calcVisibleSize( &visible_width, &visible_height, &show_h_scrollbar, &show_v_scrollbar ); + S32 border_width = mBorder->getBorderWidth(); + scroller_view_rect.setOriginAndSize(border_width, + show_h_scrollbar ? mScrollbar[HORIZONTAL]->getRect().mTop : border_width, + visible_width, + visible_height); + return scroller_view_rect; +} + +// rect is in document coordinates, constraint is in display coordinates relative to content window rect +void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLRect& constraint) +{ + if (!mScrolledView) { - vert_pos = bottom_offset; + llwarns << "LLScrollContainer::scrollToShowRect with no view!" << llendl; + return; } + LLRect content_window_rect = getContentWindowRect(); + // get document rect + LLRect scrolled_rect = mScrolledView->getRect(); + + // shrink target rect to fit within constraint region, biasing towards top left + LLRect rect_to_constrain = rect; + rect_to_constrain.mBottom = llmax(rect_to_constrain.mBottom, rect_to_constrain.mTop - constraint.getHeight()); + rect_to_constrain.mRight = llmin(rect_to_constrain.mRight, rect_to_constrain.mLeft + constraint.getWidth()); + + // calculate allowable positions for scroller window in document coordinates + LLRect allowable_scroll_rect(rect_to_constrain.mRight - constraint.mRight, + rect_to_constrain.mBottom - constraint.mBottom, + rect_to_constrain.mLeft - constraint.mLeft, + rect_to_constrain.mTop - constraint.mTop); + + // translate from allowable region for lower left corner to upper left corner + allowable_scroll_rect.translate(0, content_window_rect.getHeight()); + + S32 vert_pos = llclamp(mScrollbar[VERTICAL]->getDocPos(), + mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mTop, // min vertical scroll + mScrollbar[VERTICAL]->getDocSize() - allowable_scroll_rect.mBottom); // max vertical scroll + mScrollbar[VERTICAL]->setDocSize( scrolled_rect.getHeight() ); - mScrollbar[VERTICAL]->setPageSize( visible_height ); + mScrollbar[VERTICAL]->setPageSize( content_window_rect.getHeight() ); mScrollbar[VERTICAL]->setDocPos( vert_pos ); - // Horizontal - // 1. First make sure left side is visible - // 2. Then, if possible without hiding the left side, make the right side visible. - S32 horiz_pos = mScrollbar[HORIZONTAL]->getDocPos(); - S32 left_offset = rect.mLeft - scrolled_rect.mLeft + horiz_offset; - S32 right_offset = horiz_offset == 0 ? rect.mRight - scrolled_rect.mLeft - visible_width : left_offset; + S32 horizontal_pos = llclamp(mScrollbar[HORIZONTAL]->getDocPos(), + allowable_scroll_rect.mLeft, + allowable_scroll_rect.mRight); - if( horiz_pos >= left_offset || visible_width < rect.getWidth() ) - { - horiz_pos = left_offset; - } - else if( horiz_pos <= right_offset ) - { - horiz_pos = right_offset; - } - mScrollbar[HORIZONTAL]->setDocSize( scrolled_rect.getWidth() ); - mScrollbar[HORIZONTAL]->setPageSize( visible_width ); - mScrollbar[HORIZONTAL]->setDocPos( horiz_pos ); + mScrollbar[HORIZONTAL]->setPageSize( content_window_rect.getWidth() ); + mScrollbar[HORIZONTAL]->setDocPos( horizontal_pos ); // propagate scroll to document updateScroll(); @@ -650,21 +654,25 @@ void LLScrollContainer::scrollToShowRect(const LLRect& rect, const LLCoordGL& de void LLScrollContainer::pageUp(S32 overlap) { mScrollbar[VERTICAL]->pageUp(overlap); + updateScroll(); } void LLScrollContainer::pageDown(S32 overlap) { mScrollbar[VERTICAL]->pageDown(overlap); + updateScroll(); } void LLScrollContainer::goToTop() { mScrollbar[VERTICAL]->setDocPos(0); + updateScroll(); } void LLScrollContainer::goToBottom() { mScrollbar[VERTICAL]->setDocPos(mScrollbar[VERTICAL]->getDocSize()); + updateScroll(); } S32 LLScrollContainer::getBorderWidth() const diff --git a/indra/llui/llscrollcontainer.h b/indra/llui/llscrollcontainer.h index c2d4d2c861..8385bca02f 100644 --- a/indra/llui/llscrollcontainer.h +++ b/indra/llui/llscrollcontainer.h @@ -53,6 +53,10 @@ class LLUICtrlFactory; * the width and height of the view you're scrolling. * *****************************************************************************/ + +struct ScrollContainerRegistry : public LLChildRegistry<ScrollContainerRegistry> +{}; + class LLScrollContainer : public LLUICtrl { public: @@ -62,15 +66,19 @@ public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { - Optional<bool> is_opaque; + Optional<bool> is_opaque, + reserve_scroll_corner, + border_visible; + Optional<F32> min_auto_scroll_rate, + max_auto_scroll_rate; Optional<LLUIColor> bg_color; - Optional<bool> reserve_scroll_corner; + Optional<LLScrollbar::callback_t> scroll_callback; Params(); }; // my valid children are stored in this registry - typedef LLDefaultChildRegistry child_registry_t; + typedef ScrollContainerRegistry child_registry_t; protected: LLScrollContainer(const Params&); @@ -80,21 +88,23 @@ public: virtual void setValue(const LLSD& value) { mInnerRect.setValue(value); } - void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const; - void calcVisibleSize( const LLRect& doc_rect, S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const; void setBorderVisible( BOOL b ); - void scrollToShowRect( const LLRect& rect, const LLCoordGL& desired_offset ); + void scrollToShowRect( const LLRect& rect, const LLRect& constraint); + void scrollToShowRect( const LLRect& rect) { scrollToShowRect(rect, LLRect(0, mInnerRect.getHeight(), mInnerRect.getWidth(), 0)); } + void setReserveScrollCorner( BOOL b ) { mReserveScrollCorner = b; } - const LLRect& getScrolledViewRect() const { return mScrolledView->getRect(); } + LLRect getVisibleContentRect(); + LLRect getContentWindowRect() const; + const LLRect& getScrolledViewRect() const { return mScrolledView ? mScrolledView->getRect() : LLRect::null; } void pageUp(S32 overlap = 0); void pageDown(S32 overlap = 0); void goToTop(); void goToBottom(); + bool isAtTop() { return mScrollbar[VERTICAL]->isAtBeginning(); } + bool isAtBottom() { return mScrollbar[VERTICAL]->isAtEnd(); } S32 getBorderWidth() const; - BOOL needsToScroll(S32 x, S32 y, SCROLL_ORIENTATION axis) const; - // LLView functionality virtual void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); virtual BOOL handleKeyHere(KEY key, MASK mask); @@ -107,12 +117,15 @@ public: virtual void draw(); virtual bool addChild(LLView* view, S32 tab_group = 0); + + bool autoScroll(S32 x, S32 y); private: // internal scrollbar handlers virtual void scrollHorizontal( S32 new_pos ); virtual void scrollVertical( S32 new_pos ); void updateScroll(); + void calcVisibleSize( S32 *visible_width, S32 *visible_height, BOOL* show_h_scrollbar, BOOL* show_v_scrollbar ) const; LLScrollbar* mScrollbar[SCROLLBAR_COUNT]; LLView* mScrolledView; @@ -124,6 +137,8 @@ private: BOOL mReserveScrollCorner; BOOL mAutoScrolling; F32 mAutoScrollRate; + F32 mMinAutoScrollRate; + F32 mMaxAutoScrollRate; }; diff --git a/indra/llui/llscrollingpanellist.cpp b/indra/llui/llscrollingpanellist.cpp index 0159cdd12c..13fbe1d576 100644 --- a/indra/llui/llscrollingpanellist.cpp +++ b/indra/llui/llscrollingpanellist.cpp @@ -55,8 +55,6 @@ void LLScrollingPanelList::addPanel( LLScrollingPanel* panel ) addChildInBack( panel ); mPanelList.push_front( panel ); - const S32 GAP_BETWEEN_PANELS = 6; - // Resize this view S32 total_height = 0; S32 max_width = 0; @@ -83,6 +81,27 @@ void LLScrollingPanelList::addPanel( LLScrollingPanel* panel ) } } +void LLScrollingPanelList::removePanel(LLScrollingPanel* panel) +{ + U32 index = 0; + LLScrollingPanelList::panel_list_t::const_iterator iter; + + if (!mPanelList.empty()) + { + for (iter = mPanelList.begin(); iter != mPanelList.end(); ++iter, ++index) + { + if (*iter == panel) + { + break; + } + } + if(iter != mPanelList.end()) + { + removePanel(index); + } + } +} + void LLScrollingPanelList::removePanel( U32 panel_index ) { if ( mPanelList.empty() || panel_index >= mPanelList.size() ) diff --git a/indra/llui/llscrollingpanellist.h b/indra/llui/llscrollingpanellist.h index 5dc23facda..9da15822d0 100644 --- a/indra/llui/llscrollingpanellist.h +++ b/indra/llui/llscrollingpanellist.h @@ -68,6 +68,8 @@ public: : LLUICtrl(p) {} + static const S32 GAP_BETWEEN_PANELS = 6; + typedef std::deque<LLScrollingPanel*> panel_list_t; virtual void setValue(const LLSD& value) {}; @@ -76,6 +78,7 @@ public: void clearPanels(); void addPanel( LLScrollingPanel* panel ); + void removePanel( LLScrollingPanel* panel ); void removePanel( U32 panel_index ); void updatePanels(BOOL allow_modify); const panel_list_t& getPanelList() { return mPanelList; } diff --git a/indra/llui/llscrolllistcell.cpp b/indra/llui/llscrolllistcell.cpp index cd43e194d2..e28da91305 100644 --- a/indra/llui/llscrolllistcell.cpp +++ b/indra/llui/llscrolllistcell.cpp @@ -310,17 +310,16 @@ void LLScrollListText::draw(const LLColor4& color, const LLColor4& highlight_col break; } mFont->render(mText.getWString(), 0, - start_x, 2.f, - display_color, - mFontAlignment, - LLFontGL::BOTTOM, - 0, - LLFontGL::NO_SHADOW, - string_chars, - getWidth(), - &right_x, - FALSE, - TRUE); + start_x, 2.f, + display_color, + mFontAlignment, + LLFontGL::BOTTOM, + 0, + LLFontGL::NO_SHADOW, + string_chars, + getWidth(), + &right_x, + TRUE); } // diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp index 3041773fb2..637642cdcd 100644 --- a/indra/llui/llscrolllistctrl.cpp +++ b/indra/llui/llscrolllistctrl.cpp @@ -160,7 +160,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p) mNumDynamicWidthColumns(0), mTotalStaticColumnWidth(0), mTotalColumnPadding(0), - mSorted(FALSE), + mSorted(false), mDirty(FALSE), mOriginalSelection(-1), mLastSelected(NULL), @@ -374,6 +374,10 @@ std::vector<LLScrollListItem*> LLScrollListCtrl::getAllSelected() const S32 LLScrollListCtrl::getFirstSelectedIndex() const { S32 CurSelectedIndex = 0; + + // make sure sort is up to date before returning an index + updateSort(); + item_list::const_iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { @@ -507,7 +511,7 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r { case ADD_TOP: mItemList.push_front(item); - setSorted(FALSE); + setNeedsSort(); break; case ADD_SORTED: @@ -524,18 +528,18 @@ BOOL LLScrollListCtrl::addItem( LLScrollListItem* item, EAddPosition pos, BOOL r // ADD_SORTED just sorts by first column... // this might not match user sort criteria, so flag list as being in unsorted state - setSorted(FALSE); + setNeedsSort(); break; } case ADD_BOTTOM: mItemList.push_back(item); - setSorted(FALSE); + setNeedsSort(); break; default: llassert(0); mItemList.push_back(item); - setSorted(FALSE); + setNeedsSort(); break; } @@ -759,6 +763,9 @@ BOOL LLScrollListCtrl::selectItemRange( S32 first_index, S32 last_index ) return FALSE; } + // make sure sort is up to date + updateSort(); + S32 listlen = (S32)mItemList.size(); first_index = llclamp(first_index, 0, listlen-1); @@ -812,6 +819,7 @@ void LLScrollListCtrl::swapWithNext(S32 index) // At end of list, doesn't do anything return; } + updateSort(); LLScrollListItem *cur_itemp = mItemList[index]; mItemList[index] = mItemList[index + 1]; mItemList[index + 1] = cur_itemp; @@ -825,6 +833,7 @@ void LLScrollListCtrl::swapWithPrevious(S32 index) // At beginning of list, don't do anything } + updateSort(); LLScrollListItem *cur_itemp = mItemList[index]; mItemList[index] = mItemList[index - 1]; mItemList[index - 1] = cur_itemp; @@ -838,6 +847,8 @@ void LLScrollListCtrl::deleteSingleItem(S32 target_index) return; } + updateSort(); + LLScrollListItem *itemp; itemp = mItemList[target_index]; if (itemp == mLastSelected) @@ -939,6 +950,8 @@ S32 LLScrollListCtrl::selectMultiple( std::vector<LLUUID> ids ) S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const { + updateSort(); + S32 index = 0; item_list::const_iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -955,6 +968,8 @@ S32 LLScrollListCtrl::getItemIndex( LLScrollListItem* target_item ) const S32 LLScrollListCtrl::getItemIndex( const LLUUID& target_id ) const { + updateSort(); + S32 index = 0; item_list::const_iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) @@ -980,6 +995,8 @@ void LLScrollListCtrl::selectPrevItem( BOOL extend_selection) } else { + updateSort(); + item_list::iterator iter; for (iter = mItemList.begin(); iter != mItemList.end(); iter++) { @@ -1022,6 +1039,8 @@ void LLScrollListCtrl::selectNextItem( BOOL extend_selection) } else { + updateSort(); + item_list::reverse_iterator iter; for (iter = mItemList.rbegin(); iter != mItemList.rend(); iter++) { @@ -1436,7 +1455,7 @@ void LLScrollListCtrl::draw() LLLocalClipRect clip(getLocalRect()); // if user specifies sort, make sure it is maintained - sortItems(); + updateSort(); if (mNeedsScroll) { @@ -1463,7 +1482,7 @@ void LLScrollListCtrl::draw() if (mBorder) { - mBorder->setKeyboardFocusHighlight(gFocusMgr.getKeyboardFocus() == this); + mBorder->setKeyboardFocusHighlight(hasFocus()); } LLUICtrl::draw(); @@ -1754,6 +1773,8 @@ LLScrollListItem* LLScrollListCtrl::hitItem( S32 x, S32 y ) // Excludes disabled items. LLScrollListItem* hit_item = NULL; + updateSort(); + LLRect item_rect; item_rect.setLeftTopAndSize( mItemListRect.mLeft, @@ -2199,7 +2220,7 @@ BOOL LLScrollListCtrl::setSort(S32 column_idx, BOOL ascending) sort_column_t new_sort_column(column_idx, ascending); - setSorted(FALSE); + setNeedsSort(); if (mSortColumns.empty()) { @@ -2241,10 +2262,10 @@ void LLScrollListCtrl::sortByColumn(const std::string& name, BOOL ascending) void LLScrollListCtrl::sortByColumnIndex(U32 column, BOOL ascending) { setSort(column, ascending); - sortItems(); + updateSort(); } -void LLScrollListCtrl::sortItems() +void LLScrollListCtrl::updateSort() const { if (hasSortOrder() && !isSorted()) { @@ -2254,7 +2275,7 @@ void LLScrollListCtrl::sortItems() mItemList.end(), SortScrollListItem(mSortColumns)); - setSorted(TRUE); + mSorted = true; } } @@ -2311,7 +2332,7 @@ void LLScrollListCtrl::scrollToShowSelected() return; } - sortItems(); + updateSort(); S32 index = getFirstSelectedIndex(); if (index < 0) @@ -2552,7 +2573,7 @@ std::string LLScrollListCtrl::getSortColumnName() else return ""; } -BOOL LLScrollListCtrl::hasSortOrder() +BOOL LLScrollListCtrl::hasSortOrder() const { return !mSortColumns.empty(); } diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h index e699711bd4..253a58ab73 100644 --- a/indra/llui/llscrolllistctrl.h +++ b/indra/llui/llscrolllistctrl.h @@ -329,15 +329,16 @@ public: std::string getSortColumnName(); BOOL getSortAscending() { return mSortColumns.empty() ? TRUE : mSortColumns.back().second; } - BOOL hasSortOrder(); + BOOL hasSortOrder() const; S32 selectMultiple( std::vector<LLUUID> ids ); - void sortItems(); + // conceptually const, but mutates mItemList + void updateSort() const; // sorts a list without affecting the permanent sort order (so further list insertions can be unsorted, for example) void sortOnce(S32 column, BOOL ascending); // manually call this whenever editing list items in place to flag need for resorting - void setSorted(BOOL sorted) { mSorted = sorted; } + void setNeedsSort() { mSorted = false; } void dirtyColumns(); // some operation has potentially affected column layout or ordering protected: @@ -390,7 +391,7 @@ private: const BOOL mDisplayColumnHeaders; BOOL mColumnsDirty; - item_list mItemList; + mutable item_list mItemList; LLScrollListItem *mLastSelected; @@ -429,7 +430,7 @@ private: S32 mTotalStaticColumnWidth; S32 mTotalColumnPadding; - BOOL mSorted; + mutable bool mSorted; typedef std::map<std::string, LLScrollListColumn> column_map_t; column_map_t mColumns; diff --git a/indra/llui/llsearcheditor.h b/indra/llui/llsearcheditor.h index 368b68baa3..cd2867b493 100644 --- a/indra/llui/llsearcheditor.h +++ b/indra/llui/llsearcheditor.h @@ -67,6 +67,8 @@ public: virtual ~LLSearchEditor() {} void setText(const LLStringExplicit &new_text) { mSearchEditor->setText(new_text); } + const std::string& getText() const { return mSearchEditor->getText(); } + // LLUICtrl interface virtual void setValue(const LLSD& value ); @@ -75,6 +77,12 @@ public: virtual BOOL setLabelArg( const std::string& key, const LLStringExplicit& text ); virtual void clear(); + void setKeystrokeCallback(LLLineEditor::callback_t callback, void* user_data) + { + if(mSearchEditor) + mSearchEditor->setKeystrokeCallback(callback,user_data); + } + private: LLLineEditor* mSearchEditor; LLButton* mSearchButton; diff --git a/indra/llui/llsliderctrl.cpp b/indra/llui/llsliderctrl.cpp index 3176938ea2..675a29a8b4 100644 --- a/indra/llui/llsliderctrl.cpp +++ b/indra/llui/llsliderctrl.cpp @@ -34,8 +34,6 @@ #include "llsliderctrl.h" -#include "audioengine.h" - #include "llmath.h" #include "llfontgl.h" #include "llgl.h" diff --git a/indra/llui/llspinctrl.cpp b/indra/llui/llspinctrl.cpp index 2d70432182..7b96446fa1 100644 --- a/indra/llui/llspinctrl.cpp +++ b/indra/llui/llspinctrl.cpp @@ -45,7 +45,6 @@ #include "lltextbox.h" #include "llkeyboard.h" #include "llmath.h" -#include "audioengine.h" #include "llcontrol.h" #include "llfocusmgr.h" #include "llresmgr.h" diff --git a/indra/llui/llstatview.cpp b/indra/llui/llstatview.cpp index ab4b0be97a..4ba01eb441 100644 --- a/indra/llui/llstatview.cpp +++ b/indra/llui/llstatview.cpp @@ -66,5 +66,6 @@ LLStatView::~LLStatView() static StatViewRegistry::Register<LLStatBar> r1("stat_bar"); +static StatViewRegistry::Register<LLStatView> r2("stat_view"); diff --git a/indra/llui/llstyle.cpp b/indra/llui/llstyle.cpp index 432d54dfee..929a809d88 100644 --- a/indra/llui/llstyle.cpp +++ b/indra/llui/llstyle.cpp @@ -38,119 +38,34 @@ #include "llstring.h" #include "llui.h" - -LLStyle::LLStyle() -{ - init(TRUE, LLColor4(0,0,0,1),LLStringUtil::null); -} - -LLStyle::LLStyle(const LLStyle &style) -{ - if (this != &style) - { - init(style.isVisible(),style.getColor(),style.getFontString()); - if (style.isLink()) - { - setLinkHREF(style.getLinkHREF()); - } - mItalic = style.mItalic; - mBold = style.mBold; - mUnderline = style.mUnderline; - mDropShadow = style.mDropShadow; - mImageHeight = style.mImageHeight; - mImageWidth = style.mImageWidth; - mImagep = style.mImagep; - mIsEmbeddedItem = style.mIsEmbeddedItem; - } - else - { - init(TRUE, LLColor4(0,0,0,1),LLStringUtil::null); - } -} - -LLStyle::LLStyle(BOOL is_visible, const LLColor4 &color, const std::string& font_name) -{ - init(is_visible, color, font_name); -} - -void LLStyle::init(BOOL is_visible, const LLColor4 &color, const std::string& font_name) +LLStyle::Params::Params() +: visible("visible", true), + drop_shadow("drop_shadow", false), + color("color", LLColor4::black), + font("font", LLFontGL::getFontMonospace()), + image("image"), + link_href("href") +{} + + +LLStyle::LLStyle(const LLStyle::Params& p) +: mVisible(p.visible), + mColor(p.color()), + mFont(p.font()), + mLink(p.link_href), + mDropShadow(p.drop_shadow), + mImageHeight(0), + mImageWidth(0), + mImagep(p.image()) +{} + +void LLStyle::setFont(const LLFontGL* font) { - mVisible = is_visible; - mColor = color; - setFontName(font_name); - setLinkHREF(LLStringUtil::null); - mItalic = FALSE; - mBold = FALSE; - mUnderline = FALSE; - mDropShadow = FALSE; - mImageHeight = 0; - mImageWidth = 0; - mIsEmbeddedItem = FALSE; -} - - -// Copy assignment -LLStyle &LLStyle::operator=(const LLStyle &rhs) -{ - if (this != &rhs) - { - setVisible(rhs.isVisible()); - setColor(rhs.getColor()); - this->mFontName = rhs.getFontString(); - this->mLink = rhs.getLinkHREF(); - mImagep = rhs.mImagep; - mImageHeight = rhs.mImageHeight; - mImageWidth = rhs.mImageWidth; - mItalic = rhs.mItalic; - mBold = rhs.mBold; - mUnderline = rhs.mUnderline; - mDropShadow = rhs.mDropShadow; - mIsEmbeddedItem = rhs.mIsEmbeddedItem; - } - - return *this; + mFont = font; } -//virtual -const std::string& LLStyle::getFontString() const -{ - return mFontName; -} - -//virtual -void LLStyle::setFontName(const std::string& fontname) -{ - mFontName = fontname; - - std::string fontname_lc = fontname; - LLStringUtil::toLower(fontname_lc); - - // cache the font pointer for speed when rendering text - if ((fontname_lc == "sansserif") || (fontname_lc == "sans-serif")) - { - mFont = LLFontGL::getFontSansSerif(); - } - else if ((fontname_lc == "serif")) - { - // *TODO: Do we have a real serif font? - mFont = LLFontGL::getFontMonospace(); - } - else if ((fontname_lc == "sansserifbig")) - { - mFont = LLFontGL::getFontSansSerifBig(); - } - else if (fontname_lc == "small") - { - mFont = LLFontGL::getFontSansSerifSmall(); - } - else - { - mFont = LLFontGL::getFontMonospace(); - } -} -//virtual -LLFontGL* LLStyle::getFont() const +const LLFontGL* LLStyle::getFont() const { return mFont; } diff --git a/indra/llui/llstyle.h b/indra/llui/llstyle.h index 32ddded2c8..dcf274a651 100644 --- a/indra/llui/llstyle.h +++ b/indra/llui/llstyle.h @@ -35,59 +35,59 @@ #include "v4color.h" #include "llui.h" +#include "llinitparam.h" class LLFontGL; class LLStyle : public LLRefCount { public: - LLStyle(); - LLStyle(const LLStyle &style); - LLStyle(BOOL is_visible, const LLColor4 &color, const std::string& font_name); - - LLStyle &operator=(const LLStyle &rhs); - - virtual void init (BOOL is_visible, const LLColor4 &color, const std::string& font_name); - - virtual const LLColor4& getColor() const { return mColor; } - virtual void setColor(const LLColor4 &color) { mColor = color; } - - virtual BOOL isVisible() const; - virtual void setVisible(BOOL is_visible); + struct Params : public LLInitParam::Block<Params> + { + Optional<bool> visible, + drop_shadow; + Optional<LLUIColor> color; + Optional<const LLFontGL*> font; + Optional<LLUIImage*> image; + Optional<std::string> link_href; + Params(); + }; + LLStyle(const Params& p = Params()); +public: + const LLColor4& getColor() const { return mColor; } + void setColor(const LLColor4 &color) { mColor = color; } - virtual const std::string& getFontString() const; - virtual void setFontName(const std::string& fontname); - virtual LLFontGL* getFont() const; + BOOL isVisible() const; + void setVisible(BOOL is_visible); - virtual const std::string& getLinkHREF() const { return mLink; } - virtual void setLinkHREF(const std::string& href); - virtual BOOL isLink() const; + void setFont(const LLFontGL* font); + const LLFontGL* getFont() const; - virtual LLUIImagePtr getImage() const; - virtual void setImage(const LLUUID& src); + const std::string& getLinkHREF() const { return mLink; } + void setLinkHREF(const std::string& href); + BOOL isLink() const; - virtual BOOL isImage() const { return ((mImageWidth != 0) && (mImageHeight != 0)); } - virtual void setImageSize(S32 width, S32 height); + LLUIImagePtr getImage() const; + void setImage(const LLUUID& src); - BOOL getIsEmbeddedItem() const { return mIsEmbeddedItem; } - void setIsEmbeddedItem( BOOL b ) { mIsEmbeddedItem = b; } + BOOL isImage() const { return ((mImageWidth != 0) && (mImageHeight != 0)); } + void setImageSize(S32 width, S32 height); // inlined here to make it easier to compare to member data below. -MG bool operator==(const LLStyle &rhs) const { return - mVisible == rhs.isVisible() - && mColor == rhs.getColor() - && mFontName == rhs.getFontString() - && mLink == rhs.getLinkHREF() + mVisible == rhs.mVisible + && mColor == rhs.mColor + && mFont == rhs.mFont + && mLink == rhs.mLink && mImagep == rhs.mImagep && mImageHeight == rhs.mImageHeight && mImageWidth == rhs.mImageWidth && mItalic == rhs.mItalic && mBold == rhs.mBold && mUnderline == rhs.mUnderline - && mDropShadow == rhs.mDropShadow - && mIsEmbeddedItem == rhs.mIsEmbeddedItem; + && mDropShadow == rhs.mDropShadow; } bool operator!=(const LLStyle& rhs) const { return !(*this == rhs); } @@ -101,16 +101,15 @@ public: S32 mImageHeight; protected: - virtual ~LLStyle() { } + ~LLStyle() { } private: BOOL mVisible; LLUIColor mColor; std::string mFontName; - LLFontGL* mFont; // cached for performance + const LLFontGL* mFont; // cached for performance std::string mLink; LLUIImagePtr mImagep; - BOOL mIsEmbeddedItem; }; typedef LLPointer<LLStyle> LLStyleSP; diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp index 29c30004ef..ee36318107 100644 --- a/indra/llui/lltabcontainer.cpp +++ b/indra/llui/lltabcontainer.cpp @@ -190,7 +190,7 @@ void LLTabContainer::reshape(S32 width, S32 height, BOOL called_from_parent) } //virtual -LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const +LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse) const { tuple_list_t::const_iterator itor; for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) @@ -207,14 +207,42 @@ LLView* LLTabContainer::getChildView(const std::string& name, BOOL recurse, BOOL for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) { LLPanel *panel = (*itor)->mTabPanel; - LLView *child = panel->getChildView(name, recurse, FALSE); + LLView *child = panel->getChildView(name, recurse); if (child) { return child; } } } - return LLView::getChildView(name, recurse, create_if_missing); + return LLView::getChildView(name, recurse); +} + +//virtual +LLView* LLTabContainer::findChildView(const std::string& name, BOOL recurse) const +{ + tuple_list_t::const_iterator itor; + for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) + { + LLPanel *panel = (*itor)->mTabPanel; + if (panel->getName() == name) + { + return panel; + } + } + + if (recurse) + { + for (itor = mTabList.begin(); itor != mTabList.end(); ++itor) + { + LLPanel *panel = (*itor)->mTabPanel; + LLView *child = panel->findChildView(name, recurse); + if (child) + { + return child; + } + } + } + return LLView::findChildView(name, recurse); } bool LLTabContainer::addChild(LLView* view, S32 tab_group) @@ -457,7 +485,7 @@ BOOL LLTabContainer::handleMouseDown( S32 x, S32 y, MASK mask ) index = llclamp(index, 0, tab_count-1); LLButton* tab_button = getTab(index)->mButton; gFocusMgr.setMouseCapture(this); - gFocusMgr.setKeyboardFocus(tab_button); + tab_button->setFocus(TRUE); } } return handled; diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h index 78592a0f9a..ebe76af966 100644 --- a/indra/llui/lltabcontainer.h +++ b/indra/llui/lltabcontainer.h @@ -104,7 +104,8 @@ public: /*virtual*/ BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType type, void* cargo_data, EAcceptance* accept, std::string& tooltip); - /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const; + /*virtual*/ LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const; + /*virtual*/ LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const; /*virtual*/ void initFromParams(const LLPanel::Params& p); /*virtual*/ bool addChild(LLView* view, S32 tab_group = 0); /*virtual*/ BOOL postBuild(); diff --git a/indra/llui/lltextbox.cpp b/indra/llui/lltextbox.cpp index f9bcb685b8..96e72487b8 100644 --- a/indra/llui/lltextbox.cpp +++ b/indra/llui/lltextbox.cpp @@ -389,7 +389,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color ) mHAlign, mVAlign, 0, mShadowType, - S32_MAX, getRect().getWidth(), NULL, TRUE, mUseEllipses); + S32_MAX, getRect().getWidth(), NULL, mUseEllipses); } else { @@ -402,7 +402,7 @@ void LLTextBox::drawText( S32 x, S32 y, const LLColor4& color ) mHAlign, mVAlign, 0, mShadowType, - line_length, getRect().getWidth(), NULL, TRUE, mUseEllipses ); + line_length, getRect().getWidth(), NULL, mUseEllipses ); cur_pos += line_length + 1; y -= llfloor(mFontGL->getLineHeight()) + mLineSpacing; } diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp index 48816e4b9e..921041d17f 100644 --- a/indra/llui/lltexteditor.cpp +++ b/indra/llui/lltexteditor.cpp @@ -46,7 +46,6 @@ #include "lltimer.h" #include "llmath.h" -#include "audioengine.h" #include "llclipboard.h" #include "llscrollbar.h" #include "llstl.h" @@ -58,7 +57,11 @@ #include "llcontrol.h" #include "llwindow.h" #include "lltextparser.h" +#include "llscrollcontainer.h" +#include "llpanel.h" + #include <queue> +#include "llcombobox.h" // // Globals @@ -75,19 +78,89 @@ const S32 CURSOR_THICKNESS = 2; const S32 SPACES_PER_TAB = 4; -LLUIColor LLTextEditor::mLinkColor = LLColor4::blue; -void (* LLTextEditor::mURLcallback)(const std::string&) = NULL; -bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL; -bool (* LLTextEditor::mSecondlifeURLcallbackRightClick)(const std::string&) = NULL; +void (* LLTextEditor::sURLcallback)(const std::string&) = NULL; +bool (* LLTextEditor::sSecondlifeURLcallback)(const std::string&) = NULL; +bool (* LLTextEditor::sSecondlifeURLcallbackRightClick)(const std::string&) = NULL; + +// helper functors +struct LLTextEditor::compare_bottom +{ + bool operator()(const S32& a, const LLTextEditor::line_info& b) const + { + return a > b.mBottom; // bottom of a is higher than bottom of b + } + + bool operator()(const LLTextEditor::line_info& a, const S32& b) const + { + return a.mBottom > b; // bottom of a is higher than bottom of b + } + + bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const + { + return a.mBottom > b.mBottom; // bottom of a is higher than bottom of b + } + +}; +// helper functors +struct LLTextEditor::compare_top +{ + bool operator()(const S32& a, const LLTextEditor::line_info& b) const + { + return a > b.mTop; // top of a is higher than top of b + } + + bool operator()(const LLTextEditor::line_info& a, const S32& b) const + { + return a.mTop > b; // top of a is higher than top of b + } + + bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const + { + return a.mTop > b.mTop; // top of a is higher than top of b + } +}; + +struct LLTextEditor::line_end_compare +{ + bool operator()(const S32& pos, const LLTextEditor::line_info& info) const + { + return (pos < info.mDocIndexEnd); + } + + bool operator()(const LLTextEditor::line_info& info, const S32& pos) const + { + return (info.mDocIndexEnd < pos); + } + + bool operator()(const LLTextEditor::line_info& a, const LLTextEditor::line_info& b) const + { + return (a.mDocIndexEnd < b.mDocIndexEnd); + } + +}; + +// +// DocumentPanel +// + +class DocumentPanel : public LLPanel +{ +public: + DocumentPanel(const Params&); +}; + +DocumentPanel::DocumentPanel(const Params& p) +: LLPanel(p) +{} /////////////////////////////////////////////////////////////////// class LLTextEditor::LLTextCmdInsert : public LLTextEditor::LLTextCmd { public: - LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws) - : LLTextCmd(pos, group_with_next), mWString(ws) + LLTextCmdInsert(S32 pos, BOOL group_with_next, const LLWString &ws, LLTextSegmentPtr segment) + : LLTextCmd(pos, group_with_next, segment), mWString(ws) { } virtual ~LLTextCmdInsert() {} @@ -117,8 +190,8 @@ private: class LLTextEditor::LLTextCmdAddChar : public LLTextEditor::LLTextCmd { public: - LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc) - : LLTextCmd(pos, group_with_next), mWString(1, wc), mBlockExtensions(FALSE) + LLTextCmdAddChar( S32 pos, BOOL group_with_next, llwchar wc, LLTextSegmentPtr segment) + : LLTextCmd(pos, group_with_next, segment), mWString(1, wc), mBlockExtensions(FALSE) { } virtual void blockExtensions() @@ -127,6 +200,9 @@ public: } virtual BOOL canExtend(S32 pos) const { + // cannot extend text with custom segments + if (!mSegments.empty()) return FALSE; + return !mBlockExtensions && (pos == getPosition() + (S32)mWString.length()); } virtual BOOL execute( LLTextEditor* editor, S32* delta ) @@ -201,9 +277,10 @@ private: class LLTextEditor::LLTextCmdRemove : public LLTextEditor::LLTextCmd { public: - LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len ) : + LLTextCmdRemove( S32 pos, BOOL group_with_next, S32 len, segment_vec_t& segments ) : LLTextCmd(pos, group_with_next), mLen(len) { + std::swap(mSegments, segments); } virtual BOOL execute( LLTextEditor* editor, S32* delta ) { @@ -213,7 +290,7 @@ public: } virtual S32 undo( LLTextEditor* editor ) { - insert(editor, getPosition(), mWString ); + insert(editor, getPosition(), mWString); return getPosition() + mWString.length(); } virtual S32 redo( LLTextEditor* editor ) @@ -233,12 +310,13 @@ LLTextEditor::Params::Params() max_text_length("max_length", 255), read_only("read_only", false), embedded_items("embedded_items", false), - hide_scrollbar("hide_scrollbar", false), + hide_scrollbar("hide_scrollbar"), hide_border("hide_border", false), word_wrap("word_wrap", false), ignore_tab("ignore_tab", true), track_bottom("track_bottom", false), - takes_non_scroll_clicks("takes_non_scroll_clicks", true), + handle_edit_keys_directly("handle_edit_keys_directly", false), + show_line_numbers("show_line_numbers", false), cursor_color("cursor_color"), default_color("default_color"), text_color("text_color"), @@ -246,6 +324,8 @@ LLTextEditor::Params::Params() bg_readonly_color("bg_readonly_color"), bg_writeable_color("bg_writeable_color"), bg_focus_color("bg_focus_color"), + link_color("link_color"), + commit_on_focus_lost("commit_on_focus_lost", false), length("length"), // ignored type("type"), // ignored is_unicode("is_unicode")// ignored @@ -261,8 +341,6 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) mIsSelecting( FALSE ), mSelectionStart( 0 ), mSelectionEnd( 0 ), - mScrolledToBottom( TRUE ), - mOnScrollEndCallback( NULL ), mOnScrollEndData( NULL ), mCursorColor( p.cursor_color() ), mFgColor( p.text_color() ), @@ -271,15 +349,14 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) mWriteableBgColor( p.bg_writeable_color() ), mReadOnlyBgColor( p.bg_readonly_color() ), mFocusBgColor( p.bg_focus_color() ), + mLinkColor( p.link_color() ), mReadOnly(p.read_only), mWordWrap( p.word_wrap ), - mShowLineNumbers ( FALSE ), - mCommitOnFocusLost( FALSE ), - mHideScrollbarForShortDocs( FALSE ), - mTakesNonScrollClicks( p.takes_non_scroll_clicks ), + mShowLineNumbers ( p.show_line_numbers ), + mCommitOnFocusLost( p.commit_on_focus_lost), mTrackBottom( p.track_bottom ), mAllowEmbeddedItems( p.embedded_items ), - mHandleEditKeysDirectly( FALSE ), + mHandleEditKeysDirectly( p.handle_edit_keys_directly ), mMouseDownX(0), mMouseDownY(0), mLastSelectionX(-1), @@ -289,7 +366,8 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) mParseHTML(FALSE), mParseHighlights(FALSE), mTabsToNextField(p.ignore_tab), - mGLFont(p.font) + mDefaultFont(p.font), + mScrollIndex(-1) { static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); @@ -298,44 +376,42 @@ LLTextEditor::LLTextEditor(const LLTextEditor::Params& p) // reset desired x cursor position mDesiredXPixel = -1; - updateTextRect(); + LLScrollContainer::Params scroll_params; + scroll_params.name = "text scroller"; + scroll_params.rect = getLocalRect(); + scroll_params.follows.flags = FOLLOWS_ALL; + scroll_params.is_opaque = false; + scroll_params.mouse_opaque = false; + scroll_params.min_auto_scroll_rate = 200; + scroll_params.max_auto_scroll_rate = 800; + mScroller = LLUICtrlFactory::create<LLScrollContainer>(scroll_params); + addChild(mScroller); + + LLPanel::Params panel_params; + panel_params.name = "text_contents"; + panel_params.rect = LLRect(0, 500, 500, 0); + panel_params.background_visible = true; + panel_params.background_opaque = true; + panel_params.mouse_opaque = false; + + mDocumentPanel = LLUICtrlFactory::create<DocumentPanel>(panel_params); + mScroller->addChild(mDocumentPanel); - S32 line_height = llround( mGLFont->getLineHeight() ); - S32 page_size = mTextRect.getHeight() / line_height; - - // Init the scrollbar - LLRect scroll_rect; - scroll_rect.setOriginAndSize( - getRect().getWidth() - scrollbar_size, - 1, - scrollbar_size, - getRect().getHeight() - 1); - S32 lines_in_doc = getLineCount(); - LLScrollbar::Params sbparams; - sbparams.name("Scrollbar"); - sbparams.rect(scroll_rect); - sbparams.orientation(LLScrollbar::VERTICAL); - sbparams.doc_size(lines_in_doc); - sbparams.doc_pos(0); - sbparams.page_size(page_size); - sbparams.follows.flags(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM); - mScrollbar = LLUICtrlFactory::create<LLScrollbar> (sbparams); - mScrollbar->setOnScrollEndCallback(mOnScrollEndCallback, mOnScrollEndData); - addChild(mScrollbar); + updateTextRect(); static LLUICachedControl<S32> text_editor_border ("UITextEditorBorder", 0); LLViewBorder::Params params; - params.name("text ed border"); - params.rect(getLocalRect()); - params.bevel_style(LLViewBorder::BEVEL_IN); - params.border_thickness(text_editor_border); + params.name = "text ed border"; + params.rect = getLocalRect(); + params.bevel_style = LLViewBorder::BEVEL_IN; + params.border_thickness = text_editor_border; mBorder = LLUICtrlFactory::create<LLViewBorder> (params); addChild( mBorder ); mBorder->setVisible(!p.hide_border); - appendText(p.default_text, FALSE, FALSE); + createDefaultSegment(); - setHideScrollbarForShortDocs(p.hide_scrollbar); + appendText(p.default_text, FALSE, FALSE); mHTML.clear(); } @@ -350,16 +426,23 @@ void LLTextEditor::initFromParams( const LLTextEditor::Params& p) if (p.read_only.isProvided()) { mReadOnly = p.read_only; - updateSegments(); - updateAllowingLanguageInput(); } + + if (p.commit_on_focus_lost.isProvided()) + { + mCommitOnFocusLost = p.commit_on_focus_lost; + } + + updateSegments(); + updateAllowingLanguageInput(); + // HACK: text editors always need to be enabled so that we can scroll LLView::setEnabled(true); } LLTextEditor::~LLTextEditor() { - gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() + gFocusMgr.releaseFocusIfNeeded( this ); // calls onCommit() while LLTextEditor still valid // Route menu back to the default if( gEditMenuHandler == this ) @@ -369,8 +452,6 @@ LLTextEditor::~LLTextEditor() // Scrollbar is deleted by LLView mHoverSegment = NULL; - std::for_each(mSegments.begin(), mSegments.end(), DeletePointer()); - std::for_each(mUndoStack.begin(), mUndoStack.end(), DeletePointer()); } @@ -379,117 +460,183 @@ LLTextViewModel* LLTextEditor::getViewModel() const return (LLTextViewModel*)mViewModel.get(); } -void LLTextEditor::setTrackColor( const LLColor4& color ) -{ - mScrollbar->setTrackColor(color); -} - -void LLTextEditor::setThumbColor( const LLColor4& color ) -{ - mScrollbar->setThumbColor(color); -} - -void LLTextEditor::updateLineStartList(S32 startpos) +static LLFastTimer::DeclareTimer FTM_TEXT_REFLOW ("Text Reflow"); +void LLTextEditor::reflow(S32 start_index) { - updateSegments(); - - bindEmbeddedChars(mGLFont); + if (!mReflowNeeded) return; - S32 seg_num = mSegments.size(); - S32 seg_idx = 0; - S32 seg_offset = 0; + LLFastTimer ft(FTM_TEXT_REFLOW); + static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0); + updateSegments(); - if (!mLineStartList.empty()) + while(mReflowNeeded) { - getSegmentAndOffset(startpos, &seg_idx, &seg_offset); - line_info t(seg_idx, seg_offset); - line_list_t::iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), t, line_info_compare()); - if (iter != mLineStartList.begin()) --iter; - seg_idx = iter->mSegment; - seg_offset = iter->mOffset; - mLineStartList.erase(iter, mLineStartList.end()); - } + bool scrolled_to_bottom = mScroller->isAtBottom(); + mReflowNeeded = FALSE; - LLWString text(getWText()); - while( seg_idx < seg_num ) - { - mLineStartList.push_back(line_info(seg_idx,seg_offset)); - BOOL line_ended = FALSE; - S32 start_x = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0; - S32 line_width = start_x; - while(!line_ended && seg_idx < seg_num) + LLRect old_cursor_rect = getLocalRectFromDocIndex(mCursorPos); + bool follow_selection = mTextRect.overlaps(old_cursor_rect); // cursor is visible + S32 first_line = getFirstVisibleLine(); + // if scroll anchor not on first line, update it to first character of first line + if (!mLineInfoList.empty() + && (mScrollIndex < mLineInfoList[first_line].mDocIndexStart + || mScrollIndex >= mLineInfoList[first_line].mDocIndexEnd)) + { + mScrollIndex = mLineInfoList[first_line].mDocIndexStart; + } + LLRect first_char_rect = getLocalRectFromDocIndex(mScrollIndex); + //first_char_rect.intersectWith(mTextRect); + + S32 cur_top = -texteditor_vpad_top; + + if (getLength()) { - LLTextSegment* segment = mSegments[seg_idx]; - S32 start_idx = segment->getStart() + seg_offset; - S32 end_idx = start_idx; - while (end_idx < segment->getEnd() && text[end_idx] != '\n') + segment_set_t::iterator seg_iter = mSegments.begin(); + S32 seg_offset = 0; + S32 line_start_index = 0; + S32 text_width = mTextRect.getWidth(); // optionally reserve room for margin + S32 remaining_pixels = text_width; + LLWString text(getWText()); + S32 line_count = 0; + + // find and erase line info structs starting at start_index and going to end of document + if (!mLineInfoList.empty()) { - end_idx++; + // find first element whose end comes after start_index + line_list_t::iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), start_index, line_end_compare()); + line_start_index = iter->mDocIndexStart; + line_count = iter->mLineNum; + getSegmentAndOffset(iter->mDocIndexStart, &seg_iter, &seg_offset); + mLineInfoList.erase(iter, mLineInfoList.end()); } - if (start_idx == end_idx) + + // reserve enough space for line numbers + S32 line_height = mShowLineNumbers ? (S32)(LLFontGL::getFontMonospace()->getLineHeight()) : 0; + + while(seg_iter != mSegments.end()) { - if (end_idx >= segment->getEnd()) - { - // empty segment - seg_idx++; - seg_offset = 0; - } - else - { - // empty line - line_ended = TRUE; - seg_offset++; - } - } - else - { - const llwchar* str = text.c_str() + start_idx; - S32 drawn = mGLFont->maxDrawableChars(str, (F32)abs(mTextRect.getWidth()) - line_width, - end_idx - start_idx, mWordWrap, mAllowEmbeddedItems ); - if( 0 == drawn && line_width == start_x) + LLTextSegmentPtr segment = *seg_iter; + + // track maximum height of any segment on this line + line_height = llmax(line_height, segment->getMaxHeight()); + S32 cur_index = segment->getStart() + seg_offset; + // find run of text from this segment that we can display on one line + S32 end_index = cur_index; + while(end_index < segment->getEnd() && text[end_index] != '\n') { - // If at the beginning of a line, draw at least one character, even if it doesn't all fit. - drawn = 1; + ++end_index; } - seg_offset += drawn; - line_width += mGLFont->getWidth(str, 0, drawn, mAllowEmbeddedItems); - end_idx = segment->getStart() + seg_offset; - if (end_idx < segment->getEnd()) + + // ask segment how many character fit in remaining space + S32 max_characters = end_index - cur_index; + S32 character_count = segment->getNumChars(llmax(0, remaining_pixels), seg_offset, cur_index - line_start_index, max_characters); + + seg_offset += character_count; + + S32 last_segment_char_on_line = segment->getStart() + seg_offset; + + // if we didn't finish the current segment... + if (last_segment_char_on_line < segment->getEnd()) { - line_ended = TRUE; - if (text[end_idx] == '\n') + // set up index for next line + // ...skip newline, we don't want to draw + S32 next_line_count = line_count; + if (text[last_segment_char_on_line] == '\n') { - seg_offset++; // skip newline + seg_offset++; + last_segment_char_on_line++; + next_line_count++; } + + // add line info and keep going + mLineInfoList.push_back(line_info(line_start_index, last_segment_char_on_line, cur_top, cur_top - line_height, line_count)); + + line_start_index = segment->getStart() + seg_offset; + cur_top -= line_height; + remaining_pixels = text_width; + line_height = 0; + line_count = next_line_count; } + // ...just consumed last segment.. + else if (++segment_set_t::iterator(seg_iter) == mSegments.end()) + { + mLineInfoList.push_back(line_info(line_start_index, last_segment_char_on_line, cur_top, cur_top - line_height, line_count)); + cur_top -= line_height; + break; + } + // finished a segment and there are segments remaining on this line else { - // finished with segment - seg_idx++; + // subtract pixels used and increment segment + remaining_pixels -= segment->getWidth(seg_offset, character_count); + ++seg_iter; seg_offset = 0; } } } - } - - unbindEmbeddedChars(mGLFont); - mScrollbar->setDocSize( getLineCount() ); + // change mDocumentPanel document size to accomodate reflowed text + LLRect document_rect; + document_rect.setOriginAndSize(1, 1, + mScroller->getContentWindowRect().getWidth(), + llmax(mScroller->getContentWindowRect().getHeight(), -cur_top)); + mDocumentPanel->setShape(document_rect); - if (mHideScrollbarForShortDocs) - { - BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize()); - mScrollbar->setVisible(!short_doc); - } + // after making document big enough to hold all the text, move the text to fit in the document + if (!mLineInfoList.empty()) + { + S32 delta_pos = mDocumentPanel->getRect().getHeight() - mLineInfoList.begin()->mTop - texteditor_vpad_top; + // move line segments to fit new document rect + for (line_list_t::iterator it = mLineInfoList.begin(); it != mLineInfoList.end(); ++it) + { + it->mTop += delta_pos; + it->mBottom += delta_pos; + } + } - // if scrolled to bottom, stay at bottom - // unless user is selecting text - // do this after updating page size - if (mScrolledToBottom && mTrackBottom && !hasMouseCapture()) - { - endOfDoc(); + // calculate visible region for diplaying text + updateTextRect(); + + for (segment_set_t::iterator segment_it = mSegments.begin(); + segment_it != mSegments.end(); + ++segment_it) + { + LLTextSegmentPtr segmentp = *segment_it; + segmentp->updateLayout(*this); + + } + + // apply scroll constraints after reflowing text + if (!hasMouseCapture()) + { + LLRect visible_content_rect = mScroller->getVisibleContentRect(); + if (scrolled_to_bottom && mTrackBottom) + { + // keep bottom of text buffer visible + endOfDoc(); + } + else if (hasSelection() && follow_selection) + { + // keep cursor in same vertical position on screen when selecting text + LLRect new_cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); + new_cursor_rect_doc.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); + mScroller->scrollToShowRect(new_cursor_rect_doc, old_cursor_rect); + //llassert_always(getLocalRectFromDocIndex(mCursorPos).mBottom == old_cursor_rect.mBottom); + } + else + { + // keep first line of text visible + LLRect new_first_char_rect = getLocalRectFromDocIndex(mScrollIndex); + new_first_char_rect.translate(visible_content_rect.mLeft, visible_content_rect.mBottom); + mScroller->scrollToShowRect(new_first_char_rect, first_char_rect); + //llassert_always(getLocalRectFromDocIndex(mScrollIndex).mBottom == first_char_rect.mBottom); + } + } } + + // reset desired x cursor position + updateCursorXPos(); } //////////////////////////////////////////////////////////// @@ -519,35 +666,53 @@ BOOL LLTextEditor::truncate() return did_truncate; } +void LLTextEditor::clearSegments() +{ + mHoverSegment = NULL; + mSegments.clear(); +} + void LLTextEditor::setText(const LLStringExplicit &utf8str) { + clearSegments(); + // LLStringUtil::removeCRLF(utf8str); - mViewModel->setValue(utf8str_removeCRLF(utf8str)); + getViewModel()->setValue(utf8str_removeCRLF(utf8str)); truncate(); blockUndo(); - setCursorPos(0); + createDefaultSegment(); + + startOfDoc(); deselect(); needsReflow(); resetDirty(); + + onValueChange(0, getLength()); } void LLTextEditor::setWText(const LLWString &wtext) { + clearSegments(); + getViewModel()->setDisplay(wtext); truncate(); blockUndo(); - setCursorPos(0); + createDefaultSegment(); + + startOfDoc(); deselect(); needsReflow(); resetDirty(); + + onValueChange(0, getLength()); } // virtual @@ -562,39 +727,7 @@ std::string LLTextEditor::getText() const { llwarns << "getText() called on text with embedded items (not supported)" << llendl; } - return mViewModel->getValue().asString(); -} - -void LLTextEditor::setWordWrap(BOOL b) -{ - mWordWrap = b; - - setCursorPos(0); - deselect(); - - needsReflow(); -} - - -void LLTextEditor::setBorderVisible(BOOL b) -{ - mBorder->setVisible(b); -} - -BOOL LLTextEditor::isBorderVisible() const -{ - return mBorder->getVisible(); -} - -void LLTextEditor::setHideScrollbarForShortDocs(BOOL b) -{ - mHideScrollbarForShortDocs = b; - - if (mHideScrollbarForShortDocs) - { - BOOL short_doc = (mScrollbar->getDocSize() <= mScrollbar->getPageSize()); - mScrollbar->setVisible(!short_doc); - } + return getViewModel()->getValue().asString(); } void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap) @@ -619,7 +752,7 @@ void LLTextEditor::selectNext(const std::string& search_text_in, BOOL case_insen if (selected_text == search_text) { // We already have this word selected, we are searching for the next. - mCursorPos += search_text.size(); + setCursorPos(mCursorPos + search_text.size()); } } @@ -682,9 +815,7 @@ BOOL LLTextEditor::replaceText(const std::string& search_text_in, const std::str void LLTextEditor::replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive) { - S32 cur_pos = mScrollbar->getDocPos(); - - setCursorPos(0); + startOfDoc(); selectNext(search_text, case_insensitive, FALSE); BOOL replaced = TRUE; @@ -692,14 +823,12 @@ void LLTextEditor::replaceTextAll(const std::string& search_text, const std::str { replaced = replaceText(search_text,replace_text, case_insensitive, FALSE); } - - mScrollbar->setDocPos(cur_pos); } // Picks a new cursor position based on the screen size of text being drawn. -void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, BOOL round ) +void LLTextEditor::setCursorAtLocalPos( S32 local_x, S32 local_y, bool round, bool keep_cursor_offset ) { - setCursorPos(getCursorPosFromLocalCoord(local_x, local_y, round)); + setCursorPos(getDocIndexFromLocalCoord(local_x, local_y, round), keep_cursor_offset); } S32 LLTextEditor::prevWordPos(S32 cursorPos) const @@ -709,7 +838,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const { cursorPos--; } - while( (cursorPos > 0) && isPartOfWord( wtext[cursorPos-1] ) ) + while( (cursorPos > 0) && LLWStringUtil::isPartOfWord( wtext[cursorPos-1] ) ) { cursorPos--; } @@ -719,7 +848,7 @@ S32 LLTextEditor::prevWordPos(S32 cursorPos) const S32 LLTextEditor::nextWordPos(S32 cursorPos) const { LLWString wtext(getWText()); - while( (cursorPos < getLength()) && isPartOfWord( wtext[cursorPos] ) ) + while( (cursorPos < getLength()) && LLWStringUtil::isPartOfWord( wtext[cursorPos] ) ) { cursorPos++; } @@ -739,156 +868,316 @@ S32 LLTextEditor::getLineStart( S32 line ) const } line = llclamp(line, 0, num_lines-1); - S32 segidx = mLineStartList[line].mSegment; - S32 segoffset = mLineStartList[line].mOffset; - LLTextSegment* seg = mSegments[segidx]; - S32 res = seg->getStart() + segoffset; - if (res > seg->getEnd()) llerrs << "wtf" << llendl; - return res; + return mLineInfoList[line].mDocIndexStart; +} + +S32 LLTextEditor::getLineHeight( S32 line ) const +{ + S32 num_lines = getLineCount(); + if (num_lines == 0) + { + return 0; + } + + line = llclamp(line, 0, num_lines-1); + return mLineInfoList[line].mTop - mLineInfoList[line].mBottom; } // Given an offset into text (pos), find the corresponding line (from the start of the doc) and an offset into the line. -void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp ) const +void LLTextEditor::getLineAndOffset( S32 startpos, S32* linep, S32* offsetp, bool include_wordwrap) const { - if (mLineStartList.empty()) + if (mLineInfoList.empty()) { *linep = 0; *offsetp = startpos; } else { - S32 seg_idx, seg_offset; - getSegmentAndOffset( startpos, &seg_idx, &seg_offset ); + line_list_t::const_iterator iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), startpos, line_end_compare()); + if (include_wordwrap) + { + *linep = iter - mLineInfoList.begin(); + } + else + { + if (iter == mLineInfoList.end()) + { + *linep = mLineInfoList.back().mLineNum; + } + else + { + *linep = iter->mLineNum; + } + } + *offsetp = startpos - iter->mDocIndexStart; + } +} - line_info tline(seg_idx, seg_offset); - line_list_t::const_iterator iter = std::upper_bound(mLineStartList.begin(), mLineStartList.end(), tline, line_info_compare()); - if (iter != mLineStartList.begin()) --iter; - *linep = iter - mLineStartList.begin(); - S32 line_start = mSegments[iter->mSegment]->getStart() + iter->mOffset; - *offsetp = startpos - line_start; +void LLTextEditor::getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const +{ + *seg_iter = getSegIterContaining(startpos); + if (*seg_iter == mSegments.end()) + { + *offsetp = 0; + } + else + { + *offsetp = startpos - (**seg_iter)->getStart(); } } -void LLTextEditor::getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const +void LLTextEditor::getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ) { - if (mSegments.empty()) + *seg_iter = getSegIterContaining(startpos); + if (*seg_iter == mSegments.end()) { - *segidxp = -1; - *offsetp = startpos; + *offsetp = 0; + } + else + { + *offsetp = startpos - (**seg_iter)->getStart(); } - - LLTextSegment tseg(startpos); - segment_list_t::const_iterator seg_iter; - seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &tseg, LLTextSegment::compare()); - if (seg_iter != mSegments.begin()) --seg_iter; - *segidxp = seg_iter - mSegments.begin(); - *offsetp = startpos - (*seg_iter)->getStart(); } -const LLTextSegment* LLTextEditor::getPreviousSegment() const +const LLTextSegmentPtr LLTextEditor::getPreviousSegment() const { // find segment index at character to left of cursor (or rightmost edge of selection) - S32 idx = llmax(0, getSegmentIdxAtOffset(mCursorPos) - 1); - return idx >= 0 ? mSegments[idx] : NULL; + segment_set_t::const_iterator it = mSegments.lower_bound(new LLIndexSegment(mCursorPos)); + + if (it != mSegments.end()) + { + return *it; + } + else + { + return LLTextSegmentPtr(); + } } -void LLTextEditor::getSelectedSegments(std::vector<const LLTextSegment*>& segments) const +void LLTextEditor::getSelectedSegments(LLTextEditor::segment_vec_t& segments) const { S32 left = hasSelection() ? llmin(mSelectionStart, mSelectionEnd) : mCursorPos; S32 right = hasSelection() ? llmax(mSelectionStart, mSelectionEnd) : mCursorPos; - S32 first_idx = llmax(0, getSegmentIdxAtOffset(left)); - S32 last_idx = llmax(0, first_idx, getSegmentIdxAtOffset(right)); - for (S32 idx = first_idx; idx <= last_idx; ++idx) - { - segments.push_back(mSegments[idx]); - } + return getSegmentsInRange(segments, left, right, true); } -S32 LLTextEditor::getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const +void LLTextEditor::getSegmentsInRange(LLTextEditor::segment_vec_t& segments_out, S32 start, S32 end, bool include_partial) const { - if(mShowLineNumbers) + segment_set_t::const_iterator first_it = getSegIterContaining(start); + segment_set_t::const_iterator end_it = getSegIterContaining(end - 1); + if (end_it != mSegments.end()) ++end_it; + + for (segment_set_t::const_iterator it = first_it; it != end_it; ++it) { - local_x -= UI_TEXTEDITOR_LINE_NUMBER_MARGIN; + LLTextSegmentPtr segment = *it; + if (include_partial + || (segment->getStart() >= start + && segment->getEnd() <= end)) + { + segments_out.push_back(segment); + } } +} - // If round is true, if the position is on the right half of a character, the cursor - // will be put to its right. If round is false, the cursor will always be put to the - // character's left. +// If round is true, if the position is on the right half of a character, the cursor +// will be put to its right. If round is false, the cursor will always be put to the +// character's left. +S32 LLTextEditor::getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const +{ // Figure out which line we're nearest to. - S32 total_lines = getLineCount(); - S32 line_height = llround( mGLFont->getLineHeight() ); - S32 max_visible_lines = mTextRect.getHeight() / line_height; - S32 scroll_lines = mScrollbar->getDocPos(); - S32 visible_lines = llmin( total_lines - scroll_lines, max_visible_lines ); // Lines currently visible + LLRect visible_region = mScroller->getVisibleContentRect(); - //S32 line = S32( 0.5f + ((mTextRect.mTop - local_y) / mGLFont->getLineHeight()) ); - S32 line = (mTextRect.mTop - 1 - local_y) / line_height; - if (line >= total_lines) + // binary search for line that starts before local_y + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), local_y - mTextRect.mBottom + visible_region.mBottom, compare_bottom()); + + if (line_iter == mLineInfoList.end()) { return getLength(); // past the end } - line = llclamp( line, 0, visible_lines ) + scroll_lines; + S32 pos = getLength(); + S32 start_x = mTextRect.mLeft; + + segment_set_t::iterator line_seg_iter; + S32 line_seg_offset; + for(getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); + line_seg_iter != mSegments.end(); + ++line_seg_iter, line_seg_offset = 0) + { + const LLTextSegmentPtr segmentp = *line_seg_iter; + + S32 segment_line_start = segmentp->getStart() + line_seg_offset; + S32 segment_line_length = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd - 1) - segment_line_start; + S32 text_width = segmentp->getWidth(line_seg_offset, segment_line_length); + if (local_x < start_x + text_width // cursor to left of right edge of text + || segmentp->getEnd() >= line_iter->mDocIndexEnd - 1) // or this segment wraps to next line + { + // Figure out which character we're nearest to. + S32 offset; + if (!segmentp->canEdit()) + { + S32 segment_width = segmentp->getWidth(0, segmentp->getEnd() - segmentp->getStart()); + if (round && local_x - start_x > segment_width / 2) + { + offset = segment_line_length; + } + else + { + offset = 0; + } + } + else + { + offset = segmentp->getOffset(local_x - start_x, line_seg_offset, segment_line_length, round); + } + pos = segment_line_start + offset; + break; + } + start_x += text_width; + } - S32 line_start = getLineStart(line); - S32 next_start = getLineStart(line+1); - S32 line_end = (next_start != line_start) ? next_start - 1 : getLength(); + return pos; +} - if(line_start == -1) - { - return 0; +LLRect LLTextEditor::getLocalRectFromDocIndex(S32 pos) const +{ + LLRect local_rect(mTextRect); + local_rect.mBottom = local_rect.mTop - (S32)(mDefaultFont->getLineHeight()); + if (mLineInfoList.empty()) + { + return local_rect; } - else + + // clamp pos to valid values + pos = llclamp(pos, 0, mLineInfoList.back().mDocIndexEnd - 1); + + + // find line that contains cursor + line_list_t::const_iterator line_iter = std::upper_bound(mLineInfoList.begin(), mLineInfoList.end(), pos, line_end_compare()); + + LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); + local_rect.mLeft = mTextRect.mLeft - scrolled_view_rect.mLeft; + local_rect.mBottom = mTextRect.mBottom + (line_iter->mBottom - scrolled_view_rect.mBottom); + local_rect.mTop = mTextRect.mBottom + (line_iter->mTop - scrolled_view_rect.mBottom); + + segment_set_t::iterator line_seg_iter; + S32 line_seg_offset; + segment_set_t::iterator cursor_seg_iter; + S32 cursor_seg_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &line_seg_iter, &line_seg_offset); + getSegmentAndOffset(pos, &cursor_seg_iter, &cursor_seg_offset); + + while(line_seg_iter != mSegments.end()) { - S32 line_len = line_end - line_start; - S32 pos; - LLWString text(getWText()); + const LLTextSegmentPtr segmentp = *line_seg_iter; - if (mAllowEmbeddedItems) + if (line_seg_iter == cursor_seg_iter) { - // Figure out which character we're nearest to. - bindEmbeddedChars(mGLFont); - pos = mGLFont->charFromPixelOffset(text.c_str(), line_start, - (F32)(local_x - mTextRect.mLeft), - (F32)(mTextRect.getWidth()), - line_len, - round, TRUE); - unbindEmbeddedChars(mGLFont); + // cursor advanced to right based on difference in offset of cursor to start of line + local_rect.mLeft += segmentp->getWidth(line_seg_offset, cursor_seg_offset - line_seg_offset); + + break; } else { - pos = mGLFont->charFromPixelOffset(text.c_str(), line_start, - (F32)(local_x - mTextRect.mLeft), - (F32)mTextRect.getWidth(), - line_len, - round); + // add remainder of current text segment to cursor position + local_rect.mLeft += segmentp->getWidth(line_seg_offset, (segmentp->getEnd() - segmentp->getStart()) - line_seg_offset); + // offset will be 0 for all segments after the first + line_seg_offset = 0; + // go to next text segment on this line + ++line_seg_iter; } - - return line_start + pos; } + + local_rect.mRight = local_rect.mLeft; + + return local_rect; } -void LLTextEditor::setCursor(S32 row, S32 column) +void LLTextEditor::addDocumentChild(LLView* view) +{ + mDocumentPanel->addChild(view); +} + +void LLTextEditor::removeDocumentChild(LLView* view) +{ + mDocumentPanel->removeChild(view); +} + +bool LLTextEditor::setCursor(S32 row, S32 column) { - LLWString text(getWText()); - const llwchar* doc = text.c_str(); - const char CR = 10; - while(row--) + if (0 <= row && row < (S32)mLineInfoList.size()) { - while (CR != *doc++); + S32 doc_pos = mLineInfoList[row].mDocIndexStart; + column = llclamp(column, 0, mLineInfoList[row].mDocIndexEnd - mLineInfoList[row].mDocIndexStart - 1); + doc_pos += column; + updateCursorXPos(); + + return setCursorPos(doc_pos); } - doc += column; - setCursorPos(doc - text.c_str()); + return false; } -void LLTextEditor::setCursorPos(S32 offset) +bool LLTextEditor::setCursorPos(S32 cursor_pos, bool keep_cursor_offset) { - mCursorPos = llclamp(offset, 0, (S32)getLength()); + S32 new_cursor_pos = cursor_pos; + if (new_cursor_pos != mCursorPos) + { + new_cursor_pos = getEditableIndex(new_cursor_pos, new_cursor_pos >= mCursorPos); + } + + mCursorPos = llclamp(new_cursor_pos, 0, (S32)getLength()); needsScroll(); + if (!keep_cursor_offset) + updateCursorXPos(); + // did we get requested position? + return new_cursor_pos == cursor_pos; +} + +void LLTextEditor::updateCursorXPos() +{ // reset desired x cursor position - mDesiredXPixel = -1; + mDesiredXPixel = getLocalRectFromDocIndex(mCursorPos).mLeft; +} + +// constraint cursor to editable segments of document +S32 LLTextEditor::getEditableIndex(S32 index, bool increasing_direction) +{ + //// always allow editable position at end of doc + //if (index == getLength()) + //{ + // return index; + //} + + segment_set_t::iterator segment_iter; + S32 offset; + getSegmentAndOffset(index, &segment_iter, &offset); + + LLTextSegmentPtr segmentp = *segment_iter; + + if (segmentp->canEdit()) + { + return segmentp->getStart() + offset; + } + else if (segmentp->getStart() < index && index < segmentp->getEnd()) + { + // bias towards document end + if (increasing_direction) + { + return segmentp->getEnd(); + } + // bias towards document start + else + { + return segmentp->getStart(); + } + } + else + { + return index; + } } // virtual @@ -1029,7 +1318,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces ) } right += delta_spaces; - //text = mWText; + text = getWText(); // Find the next new line while( (cur < right) && (text[cur] != '\n') ) @@ -1055,7 +1344,7 @@ void LLTextEditor::indentSelectedLines( S32 spaces ) mSelectionStart = right; mSelectionEnd = left; } - mCursorPos = mSelectionEnd; + setCursorPos(mSelectionEnd); } } @@ -1070,7 +1359,7 @@ void LLTextEditor::selectAll() { mSelectionStart = getLength(); mSelectionEnd = 0; - mCursorPos = mSelectionEnd; + setCursorPos(mSelectionEnd); } @@ -1088,12 +1377,7 @@ BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_ } } - if( mSegments.empty() ) - { - return TRUE; - } - - const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); + const LLTextSegmentPtr cur_segment = getSegmentAtLocalPos( x, y ); if( cur_segment ) { BOOL has_tool_tip = FALSE; @@ -1114,12 +1398,6 @@ BOOL LLTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_ return TRUE; } -BOOL LLTextEditor::handleScrollWheel(S32 x, S32 y, S32 clicks) -{ - // Pretend the mouse is over the scrollbar - return mScrollbar->handleScrollWheel( 0, 0, clicks ); -} - BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -1127,7 +1405,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) // Let scrollbar have first dibs handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; - if( !handled && mTakesNonScrollClicks) + if( !handled ) { if (!(mask & MASK_SHIFT)) { @@ -1141,31 +1419,10 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) if (mask & MASK_SHIFT) { S32 old_cursor_pos = mCursorPos; - setCursorAtLocalPos( x, y, TRUE ); + setCursorAtLocalPos( x, y, true ); if (hasSelection()) { - /* Mac-like behavior - extend selection towards the cursor - if (mCursorPos < mSelectionStart - && mCursorPos < mSelectionEnd) - { - // ...left of selection - mSelectionStart = llmax(mSelectionStart, mSelectionEnd); - mSelectionEnd = mCursorPos; - } - else if (mCursorPos > mSelectionStart - && mCursorPos > mSelectionEnd) - { - // ...right of selection - mSelectionStart = llmin(mSelectionStart, mSelectionEnd); - mSelectionEnd = mCursorPos; - } - else - { - mSelectionEnd = mCursorPos; - } - */ - // Windows behavior mSelectionEnd = mCursorPos; } else @@ -1178,7 +1435,7 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) } else { - setCursorAtLocalPos( x, y, TRUE ); + setCursorAtLocalPos( x, y, true ); startSelection(); } gFocusMgr.setMouseCapture( this ); @@ -1202,11 +1459,17 @@ BOOL LLTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) { - setFocus( TRUE ); - if( canPastePrimary() ) + BOOL handled = FALSE; + handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL; + + if (!handled) { - setCursorAtLocalPos( x, y, TRUE ); - pastePrimary(); + setFocus( TRUE ); + if( canPastePrimary() ) + { + setCursorAtLocalPos( x, y, true ); + pastePrimary(); + } } return TRUE; } @@ -1217,7 +1480,12 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); BOOL handled = FALSE; + if (mHoverSegment) + { + mHoverSegment->setHasMouseHover(false); + } mHoverSegment = NULL; + if(hasMouseCapture() ) { if( mIsSelecting ) @@ -1228,17 +1496,11 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) mLastSelectionY = y; } - if( y > mTextRect.mTop ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 ); - } - else - if( y < mTextRect.mBottom ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 ); - } + mScroller->autoScroll(x, y); - setCursorAtLocalPos( x, y, TRUE ); + S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight); + S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop); + setCursorAtLocalPos( clamped_x, clamped_y, true ); mSelectionEnd = mCursorPos; } @@ -1260,51 +1522,43 @@ BOOL LLTextEditor::handleHover(S32 x, S32 y, MASK mask) } // Opaque - if( !handled && mTakesNonScrollClicks) + if( !handled ) { // Check to see if we're over an HTML-style link - if( !mSegments.empty() ) + LLTextSegmentPtr cur_segment = getSegmentAtLocalPos( x, y ); + if( cur_segment ) { - const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) + if(cur_segment->getStyle()->isLink()) { - if(cur_segment->getStyle()->isLink()) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - handled = TRUE; - } - else - if(cur_segment->getStyle()->getIsEmbeddedItem()) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - //getWindow()->setCursor(UI_CURSOR_ARROW); - handled = TRUE; - } - mHoverSegment = cur_segment; + lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl; + getWindow()->setCursor(UI_CURSOR_HAND); + handled = TRUE; } + //else + //if(cur_segment->getStyle()->getIsEmbeddedItem()) + //{ + // lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl; + // getWindow()->setCursor(UI_CURSOR_HAND); + // //getWindow()->setCursor(UI_CURSOR_ARROW); + // handled = TRUE; + //} + if (mHoverSegment) + { + mHoverSegment->setHasMouseHover(false); + } + cur_segment->setHasMouseHover(true); + mHoverSegment = cur_segment; + mHTML = mHoverSegment->getStyle()->getLinkHREF(); } if( !handled ) { lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl; - if (!mScrollbar->getVisible() || x < getRect().getWidth() - scrollbar_size) - { - getWindow()->setCursor(UI_CURSOR_IBEAM); - } - else - { - getWindow()->setCursor(UI_CURSOR_ARROW); - } + getWindow()->setCursor(UI_CURSOR_IBEAM); handled = TRUE; } } - if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) - { - mOnScrollEndCallback(mOnScrollEndData); - } return handled; } @@ -1316,22 +1570,14 @@ BOOL LLTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) // let scrollbar have first dibs handled = LLView::childrenHandleMouseUp(x, y, mask) != NULL; - if( !handled && mTakesNonScrollClicks) + if( !handled ) { if( mIsSelecting ) { - // Finish selection - if( y > mTextRect.mTop ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 ); - } - else - if( y < mTextRect.mBottom ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 ); - } - - setCursorAtLocalPos( x, y, TRUE ); + mScroller->autoScroll(x, y); + S32 clamped_x = llclamp(x, mTextRect.mLeft, mTextRect.mRight); + S32 clamped_y = llclamp(y, mTextRect.mBottom, mTextRect.mTop); + setCursorAtLocalPos( clamped_x, clamped_y, true ); endSelection(); } @@ -1367,25 +1613,25 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // let scrollbar have first dibs handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; - if( !handled && mTakesNonScrollClicks) + if( !handled ) { - setCursorAtLocalPos( x, y, FALSE ); + setCursorAtLocalPos( x, y, false ); deselect(); LLWString text = getWText(); - if( isPartOfWord( text[mCursorPos] ) ) + if( LLWStringUtil::isPartOfWord( text[mCursorPos] ) ) { // Select word the cursor is over - while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1])) + while ((mCursorPos > 0) && LLWStringUtil::isPartOfWord(text[mCursorPos-1])) { - mCursorPos--; + if (!setCursorPos(mCursorPos - 1)) break; } startSelection(); - while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) ) + while ((mCursorPos < (S32)text.length()) && LLWStringUtil::isPartOfWord( text[mCursorPos] ) ) { - mCursorPos++; + if (!setCursorPos(mCursorPos + 1)) break; } mSelectionEnd = mCursorPos; @@ -1394,7 +1640,7 @@ BOOL LLTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { // Select the character the cursor is over startSelection(); - mCursorPos++; + setCursorPos(mCursorPos + 1); mSelectionEnd = mCursorPos; } @@ -1457,19 +1703,25 @@ S32 LLTextEditor::execute( LLTextCmd* cmd ) return delta; } -S32 LLTextEditor::insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op) +S32 LLTextEditor::insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) { - return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr ) ); + return execute( new LLTextCmdInsert( pos, group_with_next_op, wstr, segment ) ); } -S32 LLTextEditor::remove(const S32 pos, const S32 length, const BOOL group_with_next_op) +S32 LLTextEditor::remove(S32 pos, S32 length, bool group_with_next_op) { - return execute( new LLTextCmdRemove( pos, group_with_next_op, length ) ); + S32 end_pos = getEditableIndex(pos + length, true); + + segment_vec_t segments_to_remove; + // store text segments + getSegmentsInRange(segments_to_remove, pos, pos + length, false); + + return execute( new LLTextCmdRemove( pos, group_with_next_op, end_pos - pos, segments_to_remove ) ); } -S32 LLTextEditor::append(const LLWString &wstr, const BOOL group_with_next_op) +S32 LLTextEditor::append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment) { - return insert(getLength(), wstr, group_with_next_op); + return insert(getLength(), wstr, group_with_next_op, segment); } S32 LLTextEditor::overwriteChar(S32 pos, llwchar wc) @@ -1575,7 +1827,7 @@ S32 LLTextEditor::addChar(S32 pos, llwchar wc) } else { - return execute(new LLTextCmdAddChar(pos, FALSE, wc)); + return execute(new LLTextCmdAddChar(pos, FALSE, wc, LLTextSegmentPtr())); } } @@ -1612,10 +1864,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) if( 0 < mCursorPos ) { startSelection(); - mCursorPos--; + setCursorPos(mCursorPos - 1); if( mask & MASK_CONTROL ) { - mCursorPos = prevWordPos(mCursorPos); + setCursorPos(prevWordPos(mCursorPos)); } mSelectionEnd = mCursorPos; } @@ -1625,10 +1877,10 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) if( mCursorPos < getLength() ) { startSelection(); - mCursorPos++; + setCursorPos(mCursorPos + 1); if( mask & MASK_CONTROL ) { - mCursorPos = nextWordPos(mCursorPos); + setCursorPos(nextWordPos(mCursorPos)); } mSelectionEnd = mCursorPos; } @@ -1650,7 +1902,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) startSelection(); if( mask & MASK_CONTROL ) { - mCursorPos = 0; + setCursorPos(0); } else { @@ -1675,7 +1927,7 @@ BOOL LLTextEditor::handleSelectionKey(const KEY key, const MASK mask) startSelection(); if( mask & MASK_CONTROL ) { - mCursorPos = getLength(); + setCursorPos(getLength()); } else { @@ -1726,14 +1978,7 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) switch( key ) { case KEY_UP: - if (mReadOnly) - { - mScrollbar->setDocPos(mScrollbar->getDocPos() - 1); - } - else - { - changeLine( -1 ); - } + changeLine( -1 ); break; case KEY_PAGE_UP: @@ -1741,25 +1986,11 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) break; case KEY_HOME: - if (mReadOnly) - { - mScrollbar->setDocPos(0); - } - else - { - startOfLine(); - } + startOfLine(); break; case KEY_DOWN: - if (mReadOnly) - { - mScrollbar->setDocPos(mScrollbar->getDocPos() + 1); - } - else - { - changeLine( 1 ); - } + changeLine( 1 ); break; case KEY_PAGE_DOWN: @@ -1767,21 +1998,10 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) break; case KEY_END: - if (mReadOnly) - { - mScrollbar->setDocPos(mScrollbar->getDocPosMax()); - } - else - { - endOfLine(); - } + endOfLine(); break; case KEY_LEFT: - if (mReadOnly) - { - break; - } if( hasSelection() ) { setCursorPos(llmin( mCursorPos - 1, mSelectionStart, mSelectionEnd )); @@ -1800,10 +2020,6 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) break; case KEY_RIGHT: - if (mReadOnly) - { - break; - } if( hasSelection() ) { setCursorPos(llmax( mCursorPos + 1, mSelectionStart, mSelectionEnd )); @@ -1827,10 +2043,6 @@ BOOL LLTextEditor::handleNavigationKey(const KEY key, const MASK mask) } } - if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) - { - mOnScrollEndCallback(mOnScrollEndData); - } return handled; } @@ -1967,7 +2179,7 @@ void LLTextEditor::pasteHelper(bool is_primary) } // Insert the new text into the existing text. - setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE)); + setCursorPos(mCursorPos + insert(mCursorPos, clean_string, FALSE, LLTextSegmentPtr())); deselect(); needsReflow(); @@ -2014,7 +2226,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask) if( mask & MASK_SHIFT ) { startSelection(); - mCursorPos = 0; + setCursorPos(0); mSelectionEnd = mCursorPos; } else @@ -2022,7 +2234,7 @@ BOOL LLTextEditor::handleControlKey(const KEY key, const MASK mask) // Ctrl-Home, Ctrl-Left, Ctrl-Right, Ctrl-Down // all move the cursor as if clicking, so should deselect. deselect(); - setCursorPos(0); + startOfDoc(); } break; @@ -2251,42 +2463,87 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) BOOL return_key_hit = FALSE; BOOL text_may_have_changed = TRUE; - if ( gFocusMgr.getKeyboardFocus() == this ) + // Special case for TAB. If want to move to next field, report + // not handled and let the parent take care of field movement. + if (KEY_TAB == key && mTabsToNextField) { - // Special case for TAB. If want to move to next field, report - // not handled and let the parent take care of field movement. - if (KEY_TAB == key && mTabsToNextField) - { - return FALSE; - } + return FALSE; + } + /* + if (KEY_F10 == key) + { + LLComboBox::Params cp; + cp.name = "combo box"; + cp.label = "my combo"; + cp.rect.width = 100; + cp.rect.height = 20; + cp.items.add().label = "item 1"; + cp.items.add().label = "item 2"; + cp.items.add().label = "item 3"; + + appendWidget(LLUICtrlFactory::create<LLComboBox>(cp), "combo", true, false); + } + if (KEY_F11 == key) + { + LLButton::Params bp; + bp.name = "text button"; + bp.label = "Click me"; + bp.rect.width = 100; + bp.rect.height = 20; + appendWidget(LLUICtrlFactory::create<LLButton>(bp), "button", true, false); + } + */ + if (mReadOnly) + { + handled = mScroller->handleKeyHere( key, mask ); + } + else + { + // handle navigation keys ourself handled = handleNavigationKey( key, mask ); + } + + + if( handled ) + { + text_may_have_changed = FALSE; + } + + if( !handled ) + { + handled = handleSelectionKey( key, mask ); if( handled ) { - text_may_have_changed = FALSE; + selection_modified = TRUE; } - - if( !handled ) + } + + if( !handled ) + { + handled = handleControlKey( key, mask ); + if( handled ) { - handled = handleSelectionKey( key, mask ); - if( handled ) - { - selection_modified = TRUE; - } + selection_modified = TRUE; } - - if( !handled ) + } + + if( !handled && mHandleEditKeysDirectly ) + { + handled = handleEditKey( key, mask ); + if( handled ) { - handled = handleControlKey( key, mask ); - if( handled ) - { - selection_modified = TRUE; - } + selection_modified = TRUE; + text_may_have_changed = TRUE; } + } - if( !handled && mHandleEditKeysDirectly ) + // Handle most keys only if the text editor is writeable. + if( !mReadOnly ) + { + if( !handled ) { - handled = handleEditKey( key, mask ); + handled = handleSpecialKey( key, mask, &return_key_hit ); if( handled ) { selection_modified = TRUE; @@ -2294,41 +2551,27 @@ BOOL LLTextEditor::handleKeyHere(KEY key, MASK mask ) } } - // Handle most keys only if the text editor is writeable. - if( !mReadOnly ) - { - if( !handled ) - { - handled = handleSpecialKey( key, mask, &return_key_hit ); - if( handled ) - { - selection_modified = TRUE; - text_may_have_changed = TRUE; - } - } + } - } + if( handled ) + { + resetKeystrokeTimer(); - if( handled ) + // Most keystrokes will make the selection box go away, but not all will. + if( !selection_modified && + KEY_SHIFT != key && + KEY_CONTROL != key && + KEY_ALT != key && + KEY_CAPSLOCK ) { - resetKeystrokeTimer(); - - // Most keystrokes will make the selection box go away, but not all will. - if( !selection_modified && - KEY_SHIFT != key && - KEY_CONTROL != key && - KEY_ALT != key && - KEY_CAPSLOCK ) - { - deselect(); - } + deselect(); + } - if(text_may_have_changed) - { - needsReflow(); - } - needsScroll(); + if(text_may_have_changed) + { + needsReflow(); } + needsScroll(); } return handled; @@ -2344,34 +2587,31 @@ BOOL LLTextEditor::handleUnicodeCharHere(llwchar uni_char) BOOL handled = FALSE; - if ( gFocusMgr.getKeyboardFocus() == this ) + // Handle most keys only if the text editor is writeable. + if( !mReadOnly ) { - // Handle most keys only if the text editor is writeable. - if( !mReadOnly ) + if( '}' == uni_char ) { - if( '}' == uni_char ) - { - unindentLineBeforeCloseBrace(); - } + unindentLineBeforeCloseBrace(); + } - // TODO: KLW Add auto show of tool tip on ( - addChar( uni_char ); + // TODO: KLW Add auto show of tool tip on ( + addChar( uni_char ); - // Keys that add characters temporarily hide the cursor - getWindow()->hideCursorUntilMouseMove(); + // Keys that add characters temporarily hide the cursor + getWindow()->hideCursorUntilMouseMove(); - handled = TRUE; - } + handled = TRUE; + } - if( handled ) - { - resetKeystrokeTimer(); + if( handled ) + { + resetKeystrokeTimer(); - // Most keystrokes will make the selection box go away, but not all will. - deselect(); + // Most keystrokes will make the selection box go away, but not all will. + deselect(); - needsReflow(); - } + needsReflow(); } return handled; @@ -2544,6 +2784,12 @@ void LLTextEditor::onFocusLost() LLUICtrl::onFocusLost(); } +void LLTextEditor::onCommit() +{ + setControlValue(getValue()); + LLUICtrl::onCommit(); +} + void LLTextEditor::setEnabled(BOOL enabled) { // just treat enabled as read-only flag @@ -2560,300 +2806,187 @@ void LLTextEditor::drawBackground() { S32 left = 0; S32 top = getRect().getHeight(); - S32 right = getRect().getWidth(); S32 bottom = 0; LLColor4 bg_color = mReadOnly ? mReadOnlyBgColor.get() - : gFocusMgr.getKeyboardFocus() == this ? mFocusBgColor.get() : mWriteableBgColor.get(); + : hasFocus() ? mFocusBgColor.get() : mWriteableBgColor.get(); if( mShowLineNumbers ) { gl_rect_2d(left, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN, bottom, mReadOnlyBgColor.get() ); // line number area always read-only - gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, right, bottom, bg_color); // body text area to the right of line numbers gl_rect_2d(UI_TEXTEDITOR_LINE_NUMBER_MARGIN, top, UI_TEXTEDITOR_LINE_NUMBER_MARGIN-1, bottom, LLColor4::grey3); // separator - } else { - gl_rect_2d(left, top, right, bottom, bg_color); // body text area - } - - LLView::draw(); + } } // Draws the black box behind the selected text void LLTextEditor::drawSelectionBackground() { // Draw selection even if we don't have keyboard focus for search/replace - if( hasSelection() ) + if( hasSelection() && !mLineInfoList.empty()) { LLWString text = getWText(); - const S32 text_len = getLength(); - std::queue<S32> line_endings; - - S32 line_height = llround( mGLFont->getLineHeight() ); + std::vector<LLRect> selection_rects; S32 selection_left = llmin( mSelectionStart, mSelectionEnd ); S32 selection_right = llmax( mSelectionStart, mSelectionEnd ); - S32 selection_left_x = mTextRect.mLeft; - S32 selection_left_y = mTextRect.mTop - line_height; - S32 selection_right_x = mTextRect.mRight; - S32 selection_right_y = mTextRect.mBottom; - - BOOL selection_left_visible = FALSE; - BOOL selection_right_visible = FALSE; + LLRect selection_rect = mTextRect; // Skip through the lines we aren't drawing. - S32 cur_line = mScrollbar->getDocPos(); - - S32 left_line_num = cur_line; - S32 num_lines = getLineCount(); - S32 right_line_num = num_lines - 1; - - S32 line_start = -1; - if (cur_line >= num_lines) - { - return; - } + LLRect content_display_rect = mScroller->getVisibleContentRect(); - line_start = getLineStart(cur_line); + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator line_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mTop, compare_bottom()); + line_list_t::const_iterator end_iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), content_display_rect.mBottom, compare_top()); - S32 left_visible_pos = line_start; - S32 right_visible_pos = line_start; - - S32 text_y = mTextRect.mTop - line_height; + bool done = false; // Find the coordinates of the selected area - while((cur_line < num_lines)) + for (;line_iter != end_iter && !done; ++line_iter) { - S32 next_line = -1; - S32 line_end = text_len; - - if ((cur_line + 1) < num_lines) + // is selection visible on this line? + if (line_iter->mDocIndexEnd > selection_left && line_iter->mDocIndexStart < selection_right) { - next_line = getLineStart(cur_line + 1); - line_end = next_line; - - line_end = ( (line_end - line_start)==0 || text[next_line-1] == '\n' || text[next_line-1] == '\0' || text[next_line-1] == ' ' || text[next_line-1] == '\t' ) ? next_line-1 : next_line; - } + segment_set_t::iterator segment_iter; + S32 segment_offset; + getSegmentAndOffset(line_iter->mDocIndexStart, &segment_iter, &segment_offset); + + LLRect selection_rect; + selection_rect.mLeft = 0; + selection_rect.mRight = 0; + selection_rect.mBottom = line_iter->mBottom; + selection_rect.mTop = line_iter->mTop; + + for(;segment_iter != mSegments.end(); ++segment_iter, segment_offset = 0) + { + LLTextSegmentPtr segmentp = *segment_iter; - const llwchar* line = text.c_str() + line_start; + S32 segment_line_start = segmentp->getStart() + segment_offset; + S32 segment_line_end = llmin(segmentp->getEnd(), line_iter->mDocIndexEnd); - if( line_start <= selection_left && selection_left <= line_end ) - { - left_line_num = cur_line; - selection_left_visible = TRUE; - selection_left_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_left - line_start, mAllowEmbeddedItems); - selection_left_y = text_y; - } - if( line_start <= selection_right && selection_right <= line_end ) - { - right_line_num = cur_line; - selection_right_visible = TRUE; - selection_right_x = mTextRect.mLeft + mGLFont->getWidth(line, 0, selection_right - line_start, mAllowEmbeddedItems); - if (selection_right == line_end) - { - // add empty space for "newline" - //selection_right_x += mGLFont->getWidth("n"); - } - selection_right_y = text_y; - } - - // if selection spans end of current line... - if (selection_left <= line_end && line_end < selection_right && selection_left != selection_right) - { - // extend selection slightly beyond end of line - // to indicate selection of newline character (use "n" character to determine width) - const LLWString nstr(utf8str_to_wstring(std::string("n"))); - line_endings.push(mTextRect.mLeft + mGLFont->getWidth(line, 0, line_end - line_start, mAllowEmbeddedItems) + mGLFont->getWidth(nstr.c_str())); - } - - // move down one line - text_y -= line_height; + // if selection after beginning of segment + if(selection_left >= segment_line_start) + { + S32 num_chars = llmin(selection_left, segment_line_end) - segment_line_start; + selection_rect.mLeft += segmentp->getWidth(segment_offset, num_chars); + } - right_visible_pos = line_end; - line_start = next_line; - cur_line++; + // if selection spans end of current segment... + if (selection_right > segment_line_end) + { + // extend selection slightly beyond end of line + // to indicate selection of newline character (use "n" character to determine width) + selection_rect.mRight += segmentp->getWidth(segment_offset, segment_line_end - segment_line_start); + } + // else if selection ends on current segment... + else + { + S32 num_chars = selection_right - segment_line_start; + selection_rect.mRight += segmentp->getWidth(segment_offset, num_chars); - if (selection_right_visible) - { - break; + break; + } + } + selection_rects.push_back(selection_rect); } } // Draw the selection box (we're using a box instead of reversing the colors on the selected text). - BOOL selection_visible = (left_visible_pos <= selection_right) && (selection_left <= right_visible_pos); - if( selection_visible ) + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); + const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get(); + F32 alpha = hasFocus() ? 0.7f : 0.3f; + gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha ); + + for (std::vector<LLRect>::iterator rect_it = selection_rects.begin(); + rect_it != selection_rects.end(); + ++rect_it) { - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - const LLColor4& color = mReadOnly ? mReadOnlyBgColor.get() : mWriteableBgColor.get(); - F32 alpha = hasFocus() ? 1.f : 0.5f; - gGL.color4f( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], alpha ); - S32 margin_offset = mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0; - - if( selection_left_y == selection_right_y ) - { - // Draw from selection start to selection end - gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1, - selection_right_x + margin_offset, selection_right_y); - } - else - { - // Draw from selection start to the end of the first line - if( mTextRect.mRight == selection_left_x ) - { - selection_left_x -= CURSOR_THICKNESS; - } - - S32 line_end = line_endings.front(); - line_endings.pop(); - gl_rect_2d( selection_left_x + margin_offset, selection_left_y + line_height + 1, - line_end + margin_offset, selection_left_y ); - - S32 line_num = left_line_num + 1; - while(line_endings.size()) - { - S32 vert_offset = -(line_num - left_line_num) * line_height; - // Draw the block between the two lines - gl_rect_2d( mTextRect.mLeft + margin_offset, selection_left_y + vert_offset + line_height + 1, - line_endings.front() + margin_offset, selection_left_y + vert_offset); - line_endings.pop(); - line_num++; - } - - // Draw from the start of the last line to selection end - if( mTextRect.mLeft == selection_right_x ) - { - selection_right_x += CURSOR_THICKNESS; - } - gl_rect_2d( mTextRect.mLeft + margin_offset, selection_right_y + line_height + 1, - selection_right_x + margin_offset, selection_right_y ); - } + LLRect selection_rect = *rect_it; + selection_rect.translate(mTextRect.mLeft - content_display_rect.mLeft, mTextRect.mBottom - content_display_rect.mBottom); + gl_rect_2d(selection_rect); } } } void LLTextEditor::drawCursor() { - if( gFocusMgr.getKeyboardFocus() == this - && gShowTextEditCursor && !mReadOnly) + if( hasFocus() + && gFocusMgr.getAppHasFocus() + && !mReadOnly) { - LLWString text = getWText(); - const S32 text_len = getLength(); + LLWString wtext = getWText(); + const llwchar* text = wtext.c_str(); - // Skip through the lines we aren't drawing. - S32 cur_pos = mScrollbar->getDocPos(); + LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); + cursor_rect.translate(-1, 0); + segment_set_t::iterator seg_it = getSegIterContaining(mCursorPos); - S32 num_lines = getLineCount(); - if (cur_pos >= num_lines) + // take style from last segment + LLTextSegmentPtr segmentp; + + if (seg_it != mSegments.end()) + { + segmentp = *seg_it; + } + else { + //segmentp = mSegments.back(); return; } - S32 line_start = getLineStart(cur_pos); - - F32 line_height = mGLFont->getLineHeight(); - F32 text_y = (F32)(mTextRect.mTop) - line_height; - F32 cursor_left = 0.f; - F32 next_char_left = 0.f; - F32 cursor_bottom = 0.f; - BOOL cursor_visible = FALSE; - - S32 line_end = 0; - // Determine if the cursor is visible and if so what its coordinates are. - while( (mTextRect.mBottom <= llround(text_y)) && (cur_pos < num_lines)) + // Draw the cursor + // (Flash the cursor every half second starting a fixed time after the last keystroke) + F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); + if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) { - line_end = text_len + 1; - S32 next_line = -1; - if ((cur_pos + 1) < num_lines) + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) { - next_line = getLineStart(cur_pos + 1); - line_end = next_line - 1; + S32 width = llmax(CURSOR_THICKNESS, segmentp->getWidth(mCursorPos - segmentp->getStart(), 1)); + cursor_rect.mRight = cursor_rect.mLeft + width; } - - const llwchar* line = text.c_str() + line_start; - - // Find the cursor and selection bounds - if( line_start <= mCursorPos && mCursorPos <= line_end ) + else { - cursor_visible = TRUE; - next_char_left = (F32)mTextRect.mLeft + mGLFont->getWidthF32(line, 0, mCursorPos - line_start, mAllowEmbeddedItems ); - cursor_left = next_char_left - 1.f; - cursor_bottom = text_y; - break; + cursor_rect.mRight = cursor_rect.mLeft + CURSOR_THICKNESS; } + + gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - // move down one line - text_y -= line_height; - line_start = next_line; - cur_pos++; - } - - if(mShowLineNumbers) - { - cursor_left += UI_TEXTEDITOR_LINE_NUMBER_MARGIN; - } + gGL.color4fv( mCursorColor.get().mV ); + + gl_rect_2d(cursor_rect); - // Draw the cursor - if( cursor_visible ) - { - // (Flash the cursor every half second starting a fixed time after the last keystroke) - F32 elapsed = mKeystrokeTimer.getElapsedTimeF32(); - if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) ) + if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') { - F32 cursor_top = cursor_bottom + line_height + 1.f; - F32 cursor_right = cursor_left + (F32)CURSOR_THICKNESS; - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection()) + LLColor4 text_color; + const LLFontGL* fontp; + if (segmentp) { - cursor_left += CURSOR_THICKNESS; - const LLWString space(utf8str_to_wstring(std::string(" "))); - F32 spacew = mGLFont->getWidthF32(space.c_str()); - if (mCursorPos == line_end) - { - cursor_right = cursor_left + spacew; - } - else - { - F32 width = mGLFont->getWidthF32(text.c_str(), mCursorPos, 1, mAllowEmbeddedItems); - cursor_right = cursor_left + llmax(spacew, width); - } + text_color = segmentp->getColor(); + fontp = segmentp->getStyle()->getFont(); } - - gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); - - gGL.color4fv( mCursorColor.get().mV ); - - gl_rect_2d(llfloor(cursor_left), llfloor(cursor_top), - llfloor(cursor_right), llfloor(cursor_bottom)); - - if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection() && text[mCursorPos] != '\n') + else if (mReadOnly) { - const LLTextSegment* segmentp = getSegmentAtOffset(mCursorPos); - LLColor4 text_color; - if (segmentp) - { - text_color = segmentp->getColor(); - } - else if (mReadOnly) - { - text_color = mReadOnlyFgColor.get(); - } - else - { - text_color = mFgColor.get(); - } - mGLFont->render(text, mCursorPos, next_char_left, cursor_bottom + line_height, - LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f), - LLFontGL::LEFT, LLFontGL::TOP, - LLFontGL::NORMAL, - LLFontGL::NO_SHADOW, - 1); + text_color = mReadOnlyFgColor.get(); + fontp = mDefaultFont; } + else + { + text_color = mFgColor.get(); + fontp = mDefaultFont; + } + fontp->render(text, mCursorPos, cursor_rect.mLeft, cursor_rect.mBottom, + LLColor4(1.f - text_color.mV[VRED], 1.f - text_color.mV[VGREEN], 1.f - text_color.mV[VBLUE], 1.f), + LLFontGL::LEFT, LLFontGL::BOTTOM, + LLFontGL::NORMAL, + LLFontGL::NO_SHADOW, + 1); + } - // Make sure the IME is in the right place - LLRect screen_pos = calcScreenRect(); - LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_left), screen_pos.mBottom + llfloor(cursor_top) ); + // Make sure the IME is in the right place + LLRect screen_pos = calcScreenRect(); + LLCoordGL ime_pos( screen_pos.mLeft + llfloor(cursor_rect.mLeft), screen_pos.mBottom + llfloor(cursor_rect.mTop) ); - ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); - ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]); - getWindow()->setLanguageTextInput( ime_pos ); - } + ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]); + ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]); + getWindow()->setLanguageTextInput( ime_pos ); } } } @@ -2879,13 +3012,13 @@ void LLTextEditor::drawPreeditMarker() const S32 text_len = getLength(); const S32 num_lines = getLineCount(); - S32 cur_line = mScrollbar->getDocPos(); + S32 cur_line = getFirstVisibleLine(); if (cur_line >= num_lines) { return; } - const S32 line_height = llround( mGLFont->getLineHeight() ); + const S32 line_height = llround( mDefaultFont->getLineHeight() ); S32 line_start = getLineStart(cur_line); S32 line_y = mTextRect.mTop - line_height; @@ -2924,16 +3057,16 @@ void LLTextEditor::drawPreeditMarker() S32 preedit_left = mTextRect.mLeft; if (left > line_start) { - preedit_left += mGLFont->getWidth(text, line_start, left - line_start, mAllowEmbeddedItems); + preedit_left += mDefaultFont->getWidth(text, line_start, left - line_start); } S32 preedit_right = mTextRect.mLeft; if (right < line_end) { - preedit_right += mGLFont->getWidth(text, line_start, right - line_start, mAllowEmbeddedItems); + preedit_right += mDefaultFont->getWidth(text, line_start, right - line_start); } else { - preedit_right += mGLFont->getWidth(text, line_start, line_end - line_start, mAllowEmbeddedItems); + preedit_right += mDefaultFont->getWidth(text, line_start, line_end - line_start); } if (mPreeditStandouts[i]) @@ -2981,25 +3114,34 @@ void LLTextEditor::drawText() } LLGLSUIDefault gls_ui; - - S32 cur_line = mScrollbar->getDocPos(); + LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); + LLRect content_rect = mScroller->getContentWindowRect(); + S32 first_line = getFirstVisibleLine(); S32 num_lines = getLineCount(); - if (cur_line >= num_lines) + if (first_line >= num_lines) { return; } - S32 line_start = getLineStart(cur_line); - LLTextSegment t(line_start); - segment_list_t::iterator seg_iter; - seg_iter = std::upper_bound(mSegments.begin(), mSegments.end(), &t, LLTextSegment::compare()); - if (seg_iter == mSegments.end() || (*seg_iter)->getStart() > line_start) --seg_iter; - LLTextSegment* cur_segment = *seg_iter; - - S32 line_height = llround( mGLFont->getLineHeight() ); - F32 text_y = (F32)(mTextRect.mTop - line_height); - while((mTextRect.mBottom <= text_y) && (cur_line < num_lines)) + S32 line_start = getLineStart(first_line); + // find first text segment that spans top of visible portion of text buffer + segment_set_t::iterator seg_iter = getSegIterContaining(line_start); + if (seg_iter == mSegments.end()) { + return; + } + + LLTextSegmentPtr cur_segment = *seg_iter; + + for (S32 cur_line = first_line; cur_line < num_lines; cur_line++) + { + line_info& line = mLineInfoList[cur_line]; + + if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) + { + break; + } + S32 next_start = -1; S32 line_end = text_len; @@ -3012,9 +3154,13 @@ void LLTextEditor::drawText() { --line_end; } - - F32 text_x = (F32)mTextRect.mLeft; + LLRect text_rect(mTextRect.mLeft - scrolled_view_rect.mLeft, + line.mTop - scrolled_view_rect.mBottom + mTextRect.mBottom, + mTextRect.getWidth() - scrolled_view_rect.mLeft, + line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom); + + // draw a single line of text S32 seg_start = line_start; while( seg_start < line_end ) { @@ -3024,184 +3170,120 @@ void LLTextEditor::drawText() if (seg_iter == mSegments.end()) { llwarns << "Ran off the segmentation end!" << llendl; + return; } cur_segment = *seg_iter; } - // Draw a segment within the line - S32 clipped_end = llmin( line_end, cur_segment->getEnd() ); - S32 clipped_len = clipped_end - seg_start; - if( clipped_len > 0 ) - { - LLStyleSP style = cur_segment->getStyle(); - if ( style->isImage() && (cur_segment->getStart() >= seg_start) && (cur_segment->getStart() <= clipped_end)) - { - S32 style_image_height = style->mImageHeight; - S32 style_image_width = style->mImageWidth; - LLUIImagePtr image = style->getImage(); - image->draw(llround(text_x), llround(text_y)+line_height-style_image_height, - style_image_width, style_image_height); - } + S32 clipped_end = llmin( line_end, cur_segment->getEnd() ) - cur_segment->getStart(); + text_rect.mLeft = (S32)(cur_segment->draw(seg_start - cur_segment->getStart(), clipped_end, selection_left, selection_right, text_rect)); - if (cur_segment == mHoverSegment && style->getIsEmbeddedItem()) - { - style->mUnderline = TRUE; - } - - S32 left_pos = llmin( mSelectionStart, mSelectionEnd ); - - if ( (mParseHTML) && (left_pos > seg_start) && (left_pos < clipped_end) && mIsSelecting && (mSelectionStart == mSelectionEnd) ) - { - mHTML = style->getLinkHREF(); - } - - drawClippedSegment( text, seg_start, clipped_end, text_x, text_y, selection_left, selection_right, style, &text_x ); - - // Note: text_x is incremented by drawClippedSegment() - seg_start += clipped_len; - } + seg_start = clipped_end + cur_segment->getStart(); } - // move down one line - text_y -= (F32)line_height; - line_start = next_start; - cur_line++; } } - -// Draws a single text segment, reversing the color for selection if needed. -void LLTextEditor::drawClippedSegment(const LLWString &text, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& style, F32* right_x ) +void LLTextEditor::drawLineNumbers() { - if (!style->isVisible()) - { - return; - } - - const LLFontGL* font = mGLFont; - - LLColor4 color = style->getColor(); + LLGLSUIDefault gls_ui; - if ( style->getFontString()[0] ) + LLRect scrolled_view_rect = mScroller->getVisibleContentRect(); + LLRect content_rect = mScroller->getContentWindowRect(); + LLLocalClipRect clip(content_rect); + S32 first_line = getFirstVisibleLine(); + S32 num_lines = getLineCount(); + if (first_line >= num_lines) { - font = style->getFont(); + return; } - - U8 font_flags = LLFontGL::NORMAL; - if (style->mBold) - { - font_flags |= LLFontGL::BOLD; - } - if (style->mItalic) - { - font_flags |= LLFontGL::ITALIC; - } - if (style->mUnderline) - { - font_flags |= LLFontGL::UNDERLINE; - } + S32 cursor_line = getCurrentLine(); - if (style->getIsEmbeddedItem()) + if (mShowLineNumbers) { - static LLUIColor text_embedded_item_readonly_color = LLUIColorTable::instance().getColor("TextEmbeddedItemReadOnlyColor"); - static LLUIColor text_embedded_item_color = LLUIColorTable::instance().getColor("TextEmbeddedItemColor"); - if (mReadOnly) - { - color = text_embedded_item_readonly_color; - } - else - { - color = text_embedded_item_color; - } - } + S32 last_line_num = -1; - F32 y_top = y + (F32)llround(font->getLineHeight()); + for (S32 cur_line = first_line; cur_line < num_lines; cur_line++) + { + line_info& line = mLineInfoList[cur_line]; - if( selection_left > seg_start ) - { - // Draw normally - S32 start = seg_start; - S32 end = llmin( selection_left, seg_end ); - S32 length = end - start; - font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems); - } - x = *right_x; - - if( (selection_left < seg_end) && (selection_right > seg_start) ) - { - // Draw reversed - S32 start = llmax( selection_left, seg_start ); - S32 end = llmin( selection_right, seg_end ); - S32 length = end - start; + if ((line.mTop - scrolled_view_rect.mBottom) < mTextRect.mBottom) + { + break; + } - font->render(text, start, x, y_top, - LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ), - LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems); - } - x = *right_x; - if( selection_right < seg_end ) - { - // Draw normally - S32 start = llmax( selection_right, seg_start ); - S32 end = seg_end; - S32 length = end - start; - font->render(text, start, x, y_top, color, LLFontGL::LEFT, LLFontGL::TOP, 0, LLFontGL::NO_SHADOW, length, S32_MAX, right_x, mAllowEmbeddedItems); + S32 line_bottom = line.mBottom - scrolled_view_rect.mBottom + mTextRect.mBottom; + // draw the line numbers + if(line.mLineNum != last_line_num && line.mTop <= scrolled_view_rect.mTop) + { + const LLFontGL *num_font = LLFontGL::getFontMonospace(); + const LLWString ltext = utf8str_to_wstring(llformat("%d", line.mLineNum )); + BOOL is_cur_line = cursor_line == line.mLineNum; + const U8 style = is_cur_line ? LLFontGL::BOLD : LLFontGL::NORMAL; + const LLColor4 fg_color = is_cur_line ? mCursorColor : mReadOnlyFgColor; + num_font->render( + ltext, // string to draw + 0, // begin offset + UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2, // x + line_bottom, // y + fg_color, + LLFontGL::RIGHT, // horizontal alignment + LLFontGL::BOTTOM, // vertical alignment + style, + LLFontGL::NO_SHADOW, + S32_MAX, // max chars + UI_TEXTEDITOR_LINE_NUMBER_MARGIN - 2); // max pixels + last_line_num = line.mLineNum; + } + } } - } - +} void LLTextEditor::draw() { - // do on-demand reflow - if (mReflowNeeded) - { - updateLineStartList(); - mReflowNeeded = FALSE; - } + // reflow if needed, on demand + reflow(); // then update scroll position, as cursor may have moved - if (mScrollNeeded) - { - updateScrollFromCursor(); - mScrollNeeded = FALSE; - } + updateScrollFromCursor(); - { - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - LLLocalClipRect clip(LLRect(0, getRect().getHeight(), getRect().getWidth() - (mScrollbar->getVisible() ? scrollbar_size : 0), 0)); + LLColor4 bg_color = mReadOnly + ? mReadOnlyBgColor.get() + : hasFocus() + ? mFocusBgColor.get() + : mWriteableBgColor.get(); - bindEmbeddedChars( mGLFont ); + mDocumentPanel->setBackgroundColor(bg_color); - drawBackground(); + drawChildren(); + drawBackground(); //overlays scrolling panel bg + drawLineNumbers(); + + { + LLLocalClipRect clip(mTextRect); drawSelectionBackground(); drawPreeditMarker(); drawText(); drawCursor(); - - unbindEmbeddedChars( mGLFont ); - - //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret - // when in readonly mode - mBorder->setKeyboardFocusHighlight( gFocusMgr.getKeyboardFocus() == this);// && !mReadOnly); } - - LLView::draw(); // Draw children (scrollbar and border) - // remember if we are supposed to be at the bottom of the buffer - mScrolledToBottom = isScrolledToBottom(); + //RN: the decision was made to always show the orange border for keyboard focus but do not put an insertion caret + // when in readonly mode + mBorder->setKeyboardFocusHighlight( hasFocus() );// && !mReadOnly); } -void LLTextEditor::onTabInto() +S32 LLTextEditor::getFirstVisibleLine() const { - // selecting all on tabInto causes users to hit tab twice and replace their text with a tab character - // theoretically, one could selectAll if mTabsToNextField is true, but we couldn't think of a use case - // where you'd want to select all anyway - // preserve insertion point when returning to the editor - //selectAll(); + LLRect visible_region = mScroller->getVisibleContentRect(); + + // binary search for line that starts before top of visible buffer + line_list_t::const_iterator iter = std::lower_bound(mLineInfoList.begin(), mLineInfoList.end(), visible_region.mTop, compare_bottom()); + + return iter - mLineInfoList.begin(); } // virtual @@ -3270,103 +3352,62 @@ S32 LLTextEditor::getPos( S32 line, S32 offset ) void LLTextEditor::changePage( S32 delta ) { + const S32 PIXEL_OVERLAP_ON_PAGE_CHANGE = 10; + if (delta == 0) return; + + //RN: use pixel heights S32 line, offset; getLineAndOffset( mCursorPos, &line, &offset ); - // get desired x position to remember previous position - S32 desired_x_pixel = mDesiredXPixel; + LLRect cursor_rect = getLocalRectFromDocIndex(mCursorPos); - // allow one line overlap - S32 page_size = mScrollbar->getPageSize() - 1; if( delta == -1 ) { - line = llmax( line - page_size, 0); - setCursorPos(getPos( line, offset )); - mScrollbar->setDocPos( mScrollbar->getDocPos() - page_size ); + mScroller->pageUp(PIXEL_OVERLAP_ON_PAGE_CHANGE); } else if( delta == 1 ) { - setCursorPos(getPos( line + page_size, offset )); - mScrollbar->setDocPos( mScrollbar->getDocPos() + page_size ); + mScroller->pageDown(PIXEL_OVERLAP_ON_PAGE_CHANGE); } - // put desired position into remember-buffer after setCursorPos() - mDesiredXPixel = desired_x_pixel; - - if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) + if (getLocalRectFromDocIndex(mCursorPos) == cursor_rect) + { + // cursor didn't change apparent position, so move to top or bottom of document, respectively + if (delta < 0) + { + startOfDoc(); + } + else + { + endOfDoc(); + } + } + else { - mOnScrollEndCallback(mOnScrollEndData); + setCursorAtLocalPos(cursor_rect.getCenterX(), cursor_rect.getCenterY(), true, false); } } void LLTextEditor::changeLine( S32 delta ) { - bindEmbeddedChars(mGLFont); - S32 line, offset; getLineAndOffset( mCursorPos, &line, &offset ); - S32 line_start = getLineStart(line); - - // set desired x position to remembered previous position - S32 desired_x_pixel = mDesiredXPixel; - // if remembered position was reset (thus -1), calculate new one here - if( desired_x_pixel == -1 ) - { - LLWString text(getWText()); - desired_x_pixel = mGLFont->getWidth(text.c_str(), line_start, offset, mAllowEmbeddedItems ); - } - - S32 new_line = 0; + S32 new_line = line; if( (delta < 0) && (line > 0 ) ) { new_line = line - 1; } - else - if( (delta > 0) && (line < (getLineCount() - 1)) ) + else if( (delta > 0) && (line < (getLineCount() - 1)) ) { new_line = line + 1; } - else - { - unbindEmbeddedChars(mGLFont); - return; - } - - S32 num_lines = getLineCount(); - S32 new_line_start = getLineStart(new_line); - S32 new_line_end = getLength(); - if (new_line + 1 < num_lines) - { - new_line_end = getLineStart(new_line + 1) - 1; - } - - S32 new_line_len = new_line_end - new_line_start; - S32 new_offset; - LLWString text(getWText()); - new_offset = mGLFont->charFromPixelOffset(text.c_str(), new_line_start, - (F32)desired_x_pixel, - (F32)mTextRect.getWidth(), - new_line_len, - mAllowEmbeddedItems); + LLRect visible_region = mScroller->getVisibleContentRect(); - setCursorPos (getPos( new_line, new_offset )); - - // put desired position into remember-buffer after setCursorPos() - mDesiredXPixel = desired_x_pixel; - unbindEmbeddedChars(mGLFont); -} - -BOOL LLTextEditor::isScrolledToTop() -{ - return mScrollbar->isAtBeginning(); -} - -BOOL LLTextEditor::isScrolledToBottom() -{ - return mScrollbar->isAtEnd(); + S32 new_cursor_pos = getDocIndexFromLocalCoord(mDesiredXPixel, mLineInfoList[new_line].mBottom + mTextRect.mBottom - visible_region.mBottom, TRUE); + setCursorPos(new_cursor_pos, true); } @@ -3383,32 +3424,11 @@ void LLTextEditor::setCursorAndScrollToEnd() { deselect(); endOfDoc(); - needsScroll(); } void LLTextEditor::getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ) { - if( include_wordwrap ) - { - getLineAndOffset( mCursorPos, line, col ); - } - else - { - LLWString text = getWText(); - S32 line_count = 0; - S32 line_start = 0; - S32 i; - for( i = 0; text[i] && (i < position); i++ ) - { - if( '\n' == text[i] ) - { - line_start = i + 1; - line_count++; - } - } - *line = line_count; - *col = i - line_start; - } + getLineAndOffset( mCursorPos, line, col, include_wordwrap ); } void LLTextEditor::getCurrentLineAndColumn( S32* line, S32* col, BOOL include_wordwrap ) @@ -3444,64 +3464,39 @@ void LLTextEditor::endOfLine() } } -void LLTextEditor::endOfDoc() +void LLTextEditor::startOfDoc() { - mScrollbar->setDocPos(mScrollbar->getDocPosMax()); - mScrolledToBottom = true; + setCursorPos(0); +} - S32 len = getLength(); - if( len ) - { - setCursorPos(len); - } - if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) - { - mOnScrollEndCallback(mOnScrollEndData); - } +void LLTextEditor::endOfDoc() +{ + setCursorPos(getLength()); } // Sets the scrollbar from the cursor position void LLTextEditor::updateScrollFromCursor() { - mScrollbar->setDocSize( getLineCount() ); - if (mReadOnly) { // no cursor in read only mode return; } - S32 line, offset; - getLineAndOffset( mCursorPos, &line, &offset ); - - S32 page_size = mScrollbar->getPageSize(); - - if( line < mScrollbar->getDocPos() ) + if (!mScrollNeeded) { - // scroll so that the cursor is at the top of the page - mScrollbar->setDocPos( line ); - } - else if( line >= mScrollbar->getDocPos() + page_size - 1 ) - { - S32 new_pos = 0; - if( line < mScrollbar->getDocSize() - 1 ) - { - // scroll so that the cursor is one line above the bottom of the page, - new_pos = line - page_size + 1; - } - else - { - // if there is less than a page of text remaining, scroll so that the cursor is at the bottom - new_pos = mScrollbar->getDocPosMax(); - } - mScrollbar->setDocPos( new_pos ); + return; } + mScrollNeeded = FALSE; - // Check if we've scrolled to bottom for callback if asked for callback - if (mOnScrollEndCallback && mOnScrollEndData && (mScrollbar->getDocPos() == mScrollbar->getDocPosMax())) - { - mOnScrollEndCallback(mOnScrollEndData); - } + S32 line, offset; + getLineAndOffset( mCursorPos, &line, &offset ); + + // scroll so that the cursor is at the top of the page + LLRect scroller_doc_window = mScroller->getVisibleContentRect(); + LLRect cursor_rect_doc = getLocalRectFromDocIndex(mCursorPos); + cursor_rect_doc.translate(scroller_doc_window.mLeft, scroller_doc_window.mBottom); + mScroller->scrollToShowRect(cursor_rect_doc, LLRect(0, scroller_doc_window.getHeight() - 5, scroller_doc_window.getWidth(), 5)); } void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) @@ -3513,13 +3508,6 @@ void LLTextEditor::reshape(S32 width, S32 height, BOOL called_from_parent) updateTextRect(); needsReflow(); - - // propagate shape information to scrollbar - mScrollbar->setDocSize( getLineCount() ); - - S32 line_height = llround( mGLFont->getLineHeight() ); - S32 page_lines = mTextRect.getHeight() / line_height; - mScrollbar->setPageSize( page_lines ); } void LLTextEditor::autoIndent() @@ -3564,7 +3552,7 @@ void LLTextEditor::insertText(const std::string &new_text) deleteSelection(TRUE); } - setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE )); + setCursorPos(mCursorPos + insert( mCursorPos, utf8str_to_wstring(new_text), FALSE, LLTextSegmentPtr() )); needsReflow(); @@ -3585,17 +3573,23 @@ void LLTextEditor::appendColoredText(const std::string &new_text, highlight->parseFullLineHighlights(new_text, &lcolor); } - LLStyleSP style(new LLStyle); - style->setVisible(true); - style->setColor(lcolor); - style->setFontName(font_name); - appendStyledText(new_text, allow_undo, prepend_newline, style); + LLStyle::Params style_params; + style_params.color = lcolor; + if (font_name.empty()) + { + style_params.font = mDefaultFont; + } + else + { + style_params.font.name = font_name; + } + appendStyledText(new_text, allow_undo, prepend_newline, style_params); } void LLTextEditor::appendStyledText(const std::string &new_text, bool allow_undo, bool prepend_newline, - LLStyleSP stylep) + const LLStyle::Params& style_params) { S32 part = (S32)LLTextParser::WHOLE; if(mParseHTML) @@ -3605,14 +3599,10 @@ void LLTextEditor::appendStyledText(const std::string &new_text, std::string text = new_text; while ( findHTML(text, &start, &end) ) { - LLStyleSP html(new LLStyle); - html->setVisible(true); - html->setColor(mLinkColor); - if (stylep) - { - html->setFontName(stylep->getFontString()); - } - html->mUnderline = TRUE; + LLStyle::Params link_params = style_params; + link_params.color = mLinkColor; + link_params.font.style = "UNDERLINE"; + link_params.link_href = text.substr(start,end-start); if (start > 0) { @@ -3626,11 +3616,10 @@ void LLTextEditor::appendStyledText(const std::string &new_text, part = (S32)LLTextParser::MIDDLE; } std::string subtext=text.substr(0,start); - appendHighlightedText(subtext,allow_undo, prepend_newline, part, stylep); + appendHighlightedText(subtext,allow_undo, prepend_newline, part, style_params); } - html->setLinkHREF(text.substr(start,end-start)); - appendText(text.substr(start, end-start),allow_undo, prepend_newline, html); + appendText(text.substr(start, end-start),allow_undo, prepend_newline, link_params); if (end < (S32)text.length()) { text = text.substr(end,text.length() - end); @@ -3643,11 +3632,11 @@ void LLTextEditor::appendStyledText(const std::string &new_text, } } if (part != (S32)LLTextParser::WHOLE) part=(S32)LLTextParser::END; - if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, stylep); + if (end < (S32)text.length()) appendHighlightedText(text,allow_undo, prepend_newline, part, style_params); } else { - appendHighlightedText(new_text, allow_undo, prepend_newline, part, stylep); + appendHighlightedText(new_text, allow_undo, prepend_newline, part, style_params); } } @@ -3655,38 +3644,40 @@ void LLTextEditor::appendHighlightedText(const std::string &new_text, bool allow_undo, bool prepend_newline, S32 highlight_part, - LLStyleSP stylep) + const LLStyle::Params& style_params) { if (mParseHighlights) { LLTextParser* highlight = LLTextParser::getInstance(); - if (highlight && stylep) + if (highlight && !style_params.isDefault()) { - LLSD pieces = highlight->parsePartialLineHighlights(new_text, stylep->getColor(), highlight_part); + LLStyle::Params highlight_params = style_params; + + LLSD pieces = highlight->parsePartialLineHighlights(new_text, highlight_params.color(), highlight_part); bool lprepend=prepend_newline; for (S32 i=0;i<pieces.size();i++) { LLSD color_llsd = pieces[i]["color"]; LLColor4 lcolor; lcolor.setValue(color_llsd); - LLStyleSP lstylep(new LLStyle(*stylep)); - lstylep->setColor(lcolor); + highlight_params.color = lcolor; if (i != 0 && (pieces.size() > 1) ) lprepend=FALSE; - appendText((std::string)pieces[i]["text"], allow_undo, lprepend, lstylep); + appendText((std::string)pieces[i]["text"], allow_undo, lprepend, highlight_params); } return; } } - appendText(new_text, allow_undo, prepend_newline, stylep); + appendText(new_text, allow_undo, prepend_newline, style_params); } // Appends new text to end of document void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool prepend_newline, - const LLStyleSP stylep) + const LLStyle::Params& stylep) { + if (new_text.empty()) return; + // Save old state - BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()); S32 selection_start = mSelectionStart; S32 selection_end = mSelectionEnd; BOOL was_selecting = mIsSelecting; @@ -3698,50 +3689,94 @@ void LLTextEditor::appendText(const std::string &new_text, bool allow_undo, bool setCursorPos(old_length); + LLWString wide_text; + // Add carriage return if not first line if (getLength() != 0 && prepend_newline) { - std::string final_text = "\n"; - final_text += new_text; - append(utf8str_to_wstring(final_text), TRUE); + wide_text = utf8str_to_wstring(std::string("\n") + new_text); } else { - append(utf8str_to_wstring(new_text), TRUE ); + wide_text = utf8str_to_wstring(new_text); } - if (stylep) + LLTextSegmentPtr segmentp; + if (!stylep.isDefault()) { S32 segment_start = old_length; - S32 segment_end = getLength(); - LLTextSegment* segment = new LLTextSegment(stylep, segment_start, segment_end ); - mSegments.push_back(segment); + S32 segment_end = old_length + wide_text.size(); + segmentp = new LLNormalTextSegment(new LLStyle(stylep), segment_start, segment_end, *this ); } + + append(wide_text, TRUE, segmentp); needsReflow(); // Set the cursor and scroll position - // Maintain the scroll position unless the scroll was at the end of the doc (in which - // case, move it to the new end of the doc) or unless the user was doing actively selecting - if( was_scrolled_to_bottom && !was_selecting ) - { - if( selection_start != selection_end ) - { - // maintain an existing non-active selection - mSelectionStart = selection_start; - mSelectionEnd = selection_end; - } - endOfDoc(); - } - else if( selection_start != selection_end ) + if( selection_start != selection_end ) { mSelectionStart = selection_start; mSelectionEnd = selection_end; + mIsSelecting = was_selecting; + setCursorPos(cursor_pos); + } + else if( cursor_was_at_end ) + { + setCursorPos(getLength()); + } + else + { + setCursorPos(cursor_pos); + } + if( !allow_undo ) + { + blockUndo(); + } +} +void LLTextEditor::appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline) +{ + // Save old state + S32 selection_start = mSelectionStart; + S32 selection_end = mSelectionEnd; + BOOL was_selecting = mIsSelecting; + S32 cursor_pos = mCursorPos; + S32 old_length = getLength(); + BOOL cursor_was_at_end = (mCursorPos == old_length); + + deselect(); + + setCursorPos(old_length); + + LLWString widget_wide_text; + + // Add carriage return if not first line + if (getLength() != 0 + && prepend_newline) + { + widget_wide_text = utf8str_to_wstring(std::string("\n") + widget_text); + } + else + { + widget_wide_text = utf8str_to_wstring(widget_text); + } + + LLTextSegmentPtr segment = new LLInlineViewSegment(widget, old_length, old_length + widget_text.size()); + append(widget_wide_text, FALSE, segment); + + needsReflow(); + + // Set the cursor and scroll position + if( selection_start != selection_end ) + { + mSelectionStart = selection_start; + mSelectionEnd = selection_end; + mIsSelecting = was_selecting; setCursorPos(cursor_pos); } @@ -3767,26 +3802,79 @@ void LLTextEditor::removeTextFromEnd(S32 num_chars) remove(getLength() - num_chars, num_chars, FALSE); S32 len = getLength(); - mCursorPos = llclamp(mCursorPos, 0, len); + setCursorPos (llclamp(mCursorPos, 0, len)); mSelectionStart = llclamp(mSelectionStart, 0, len); mSelectionEnd = llclamp(mSelectionEnd, 0, len); - pruneSegments(); - - // pruneSegments will invalidate mLineStartList. - updateLineStartList(); + reflow(); needsScroll(); } /////////////////////////////////////////////////////////////////// // Returns change in number of characters in mWText -S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) +S32 LLTextEditor::insertStringNoUndo(S32 pos, const LLWString &wstr, LLTextEditor::segment_vec_t* segments ) { - LLWString text(getWText()); + LLWString text(getWText()); S32 old_len = text.length(); // length() returns character length S32 insert_len = wstr.length(); + pos = getEditableIndex(pos, true); + + segment_set_t::iterator seg_iter = getSegIterContaining(pos); + + LLTextSegmentPtr default_segment; + + LLTextSegmentPtr segmentp; + if (seg_iter != mSegments.end()) + { + segmentp = *seg_iter; + } + else + { + //segmentp = mSegments.back(); + return pos; + } + + if (segmentp->canEdit()) + { + segmentp->setEnd(segmentp->getEnd() + insert_len); + if (seg_iter != mSegments.end()) + { + ++seg_iter; + } + } + else + { + // create default editable segment to hold new text + default_segment = new LLNormalTextSegment( getDefaultStyle(), pos, pos + insert_len, *this); + } + + // shift remaining segments to right + for(;seg_iter != mSegments.end(); ++seg_iter) + { + LLTextSegmentPtr segmentp = *seg_iter; + segmentp->setStart(segmentp->getStart() + insert_len); + segmentp->setEnd(segmentp->getEnd() + insert_len); + } + + // insert new segments + if (segments) + { + if (default_segment.notNull()) + { + // potentially overwritten by segments passed in + insertSegment(default_segment); + } + for (segment_vec_t::iterator seg_iter = segments->begin(); + seg_iter != segments->end(); + ++seg_iter) + { + LLTextSegment* segmentp = *seg_iter; + insertSegment(segmentp); + } + } + text.insert(pos, wstr); getViewModel()->setDisplay(text); @@ -3797,14 +3885,67 @@ S32 LLTextEditor::insertStringNoUndo(const S32 pos, const LLWString &wstr) insert_len = getLength() - old_len; } + onValueChange(pos, pos + insert_len); + return insert_len; } S32 LLTextEditor::removeStringNoUndo(S32 pos, S32 length) { LLWString text(getWText()); + segment_set_t::iterator seg_iter = getSegIterContaining(pos); + while(seg_iter != mSegments.end()) + { + LLTextSegmentPtr segmentp = *seg_iter; + S32 end = pos + length; + if (segmentp->getStart() < pos) + { + // deleting from middle of segment + if (segmentp->getEnd() > end) + { + segmentp->setEnd(segmentp->getEnd() - length); + } + // truncating segment + else + { + segmentp->setEnd(pos); + } + } + else if (segmentp->getStart() < end) + { + // deleting entire segment + if (segmentp->getEnd() <= end) + { + // remove segment + segmentp->unlinkFromDocument(this); + segment_set_t::iterator seg_to_erase(seg_iter++); + mSegments.erase(seg_to_erase); + continue; + } + // deleting head of segment + else + { + segmentp->setStart(pos); + segmentp->setEnd(segmentp->getEnd() - length); + } + } + else + { + // shifting segments backward to fill deleted portion + segmentp->setStart(segmentp->getStart() - length); + segmentp->setEnd(segmentp->getEnd() - length); + } + ++seg_iter; + } + text.erase(pos, length); getViewModel()->setDisplay(text); + + // recreate default segment in case we erased everything + createDefaultSegment(); + + onValueChange(pos, pos); + return -length; // This will be wrong if someone calls removeStringNoUndo with an excessive length } @@ -3817,6 +3958,9 @@ S32 LLTextEditor::overwriteCharNoUndo(S32 pos, llwchar wc) LLWString text(getWText()); text[pos] = wc; getViewModel()->setDisplay(text); + + onValueChange(pos, pos + 1); + return 1; } @@ -3887,21 +4031,25 @@ void LLTextEditor::updateTextRect() { static LLUICachedControl<S32> texteditor_border ("UITextEditorBorder", 0); static LLUICachedControl<S32> texteditor_h_pad ("UITextEditorHPad", 0); - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - static LLUICachedControl<S32> texteditor_vpad_top ("UITextEditorVPadTop", 0); - - mTextRect.setOriginAndSize( - texteditor_border + texteditor_h_pad, - texteditor_border, - getRect().getWidth() - scrollbar_size - 2 * (texteditor_border + texteditor_h_pad), - getRect().getHeight() - 2 * texteditor_border - texteditor_vpad_top ); + + LLRect old_text_rect = mTextRect; + mTextRect = mScroller->getContentWindowRect(); + mTextRect.stretch(texteditor_border * -1); + mTextRect.mLeft += texteditor_h_pad; + mTextRect.mLeft += mShowLineNumbers ? UI_TEXTEDITOR_LINE_NUMBER_MARGIN : 0; + if (mTextRect != old_text_rect) + { + needsReflow(); + } } +LLFastTimer::DeclareTimer FTM_TEXT_EDITOR_LOAD_KEYWORD("Text Editor Load Keywords"); void LLTextEditor::loadKeywords(const std::string& filename, const std::vector<std::string>& funcs, const std::vector<std::string>& tooltips, const LLColor3& color) { + LLFastTimer ft(FTM_TEXT_EDITOR_LOAD_KEYWORD); if(mKeywords.loadFromFile(filename)) { S32 count = llmin(funcs.size(), tooltips.size()); @@ -3910,133 +4058,115 @@ void LLTextEditor::loadKeywords(const std::string& filename, std::string name = utf8str_trim(funcs[i]); mKeywords.addToken(LLKeywordToken::WORD, name, color, tooltips[i] ); } + segment_vec_t segment_list; + mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this); - mKeywords.findSegments( &mSegments, getWText(), mDefaultColor.get() ); - - llassert( mSegments.front()->getStart() == 0 ); - llassert( mSegments.back()->getEnd() == getLength() ); + mSegments.clear(); + segment_set_t::iterator insert_it = mSegments.begin(); + for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + { + insert_it = mSegments.insert(insert_it, *list_it); + } } } -void LLTextEditor::updateSegments() +void LLTextEditor::createDefaultSegment() { - if (mKeywords.isLoaded()) - { - // HACK: No non-ascii keywords for now - mKeywords.findSegments(&mSegments, getWText(), mDefaultColor.get()); - } - else if (mAllowEmbeddedItems) - { - findEmbeddedItemSegments(); - } - - // Make sure we have at least one segment - if (mSegments.size() == 1 && mSegments[0]->getIsDefault()) - { - delete mSegments[0]; - mSegments.clear(); // create default segment - } + // ensures that there is always at least one segment if (mSegments.empty()) { - LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); - LLTextSegment* default_segment = new LLTextSegment( text_color, 0, getLength() ); - default_segment->setIsDefault(TRUE); - mSegments.push_back(default_segment); + LLTextSegmentPtr default_segment = new LLNormalTextSegment( getDefaultStyle(), 0, getLength() + 1, *this); + mSegments.insert(default_segment); + default_segment->linkToDocument(this); } } -// Only effective if text was removed from the end of the editor -// *NOTE: Using this will invalidate references to mSegments from mLineStartList. -void LLTextEditor::pruneSegments() +LLStyleSP LLTextEditor::getDefaultStyle() { - S32 len = getLength(); - // Find and update the first valid segment - segment_list_t::iterator iter = mSegments.end(); - while(iter != mSegments.begin()) - { - --iter; - LLTextSegment* seg = *iter; - if (seg->getStart() < len) - { - // valid segment - if (seg->getEnd() > len) - { - seg->setEnd(len); - } - break; // done - } - } - if (iter != mSegments.end()) - { - // erase invalid segments - ++iter; - std::for_each(iter, mSegments.end(), DeletePointer()); - mSegments.erase(iter, mSegments.end()); - } - else - { - llwarns << "Tried to erase end of empty LLTextEditor" << llendl; - } + LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); + return LLStyleSP(new LLStyle(LLStyle::Params().color(text_color).font(mDefaultFont))); } -void LLTextEditor::findEmbeddedItemSegments() +LLFastTimer::DeclareTimer FTM_UPDATE_TEXT_SEGMENTS("Update Text Segments"); +void LLTextEditor::updateSegments() { - mHoverSegment = NULL; - std::for_each(mSegments.begin(), mSegments.end(), DeletePointer()); - mSegments.clear(); - - BOOL found_embedded_items = FALSE; - LLWString text = getWText(); - S32 idx = 0; - while( text[idx] ) + LLFastTimer ft(FTM_UPDATE_TEXT_SEGMENTS); + if (mKeywords.isLoaded()) { - if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR ) - { - found_embedded_items = TRUE; - break; + // HACK: No non-ascii keywords for now + segment_vec_t segment_list; + mKeywords.findSegments(&segment_list, getWText(), mDefaultColor.get(), *this); + + mSegments.clear(); + segment_set_t::iterator insert_it = mSegments.begin(); + for (segment_vec_t::iterator list_it = segment_list.begin(); list_it != segment_list.end(); ++list_it) + { + insert_it = mSegments.insert(insert_it, *list_it); } - ++idx; } - if( !found_embedded_items ) + createDefaultSegment(); + +} + +void LLTextEditor::insertSegment(LLTextSegmentPtr segment_to_insert) +{ + if (segment_to_insert.isNull()) { return; } - S32 text_len = text.length(); - - BOOL in_text = FALSE; - - LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); + segment_set_t::iterator cur_seg_iter = getSegIterContaining(segment_to_insert->getStart()); - if( idx > 0 ) + if (cur_seg_iter == mSegments.end()) { - mSegments.push_back( new LLTextSegment( text_color, 0, text_len ) ); // text - in_text = TRUE; + mSegments.insert(segment_to_insert); + segment_to_insert->linkToDocument(this); } - - LLStyleSP embedded_style(new LLStyle); - embedded_style->setIsEmbeddedItem( TRUE ); - - // Start with i just after the first embedded item - while ( text[idx] ) + else { - if( text[idx] >= FIRST_EMBEDDED_CHAR && text[idx] <= LAST_EMBEDDED_CHAR ) + LLTextSegmentPtr cur_segmentp = *cur_seg_iter; + if (cur_segmentp->getStart() < segment_to_insert->getStart()) { - if( in_text ) - { - mSegments.back()->setEnd( idx ); - } - mSegments.push_back( new LLTextSegment( embedded_style, idx, idx + 1 ) ); // item - in_text = FALSE; + S32 old_segment_end = cur_segmentp->getEnd(); + // split old at start point for new segment + cur_segmentp->setEnd(segment_to_insert->getStart()); + // advance to next segment + ++cur_seg_iter; + // insert remainder of old segment + LLTextSegmentPtr remainder_segment = new LLNormalTextSegment( cur_segmentp->getStyle(), segment_to_insert->getStart(), old_segment_end, *this); + cur_seg_iter = mSegments.insert(cur_seg_iter, remainder_segment); + remainder_segment->linkToDocument(this); + // insert new segment before remainder of old segment + cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); + + segment_to_insert->linkToDocument(this); + // move to "remanider" segment and start truncation there + ++cur_seg_iter; } else - if( !in_text ) { - mSegments.push_back( new LLTextSegment( text_color, idx, text_len ) ); // text - in_text = TRUE; + cur_seg_iter = mSegments.insert(cur_seg_iter, segment_to_insert); + ++cur_seg_iter; + segment_to_insert->linkToDocument(this); + } + + // now delete/truncate remaining segments as necessary + while(cur_seg_iter != mSegments.end()) + { + cur_segmentp = *cur_seg_iter; + if (cur_segmentp->getEnd() <= segment_to_insert->getEnd()) + { + cur_segmentp->unlinkFromDocument(this); + segment_set_t::iterator seg_to_erase(cur_seg_iter++); + mSegments.erase(seg_to_erase); + } + else + { + cur_segmentp->setStart(segment_to_insert->getEnd()); + break; + } } - ++idx; } } @@ -4050,9 +4180,9 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) if (mParseHTML && mHTML.length() > 0) { //Special handling for slurls - if ( (mSecondlifeURLcallback!=NULL) && !(*mSecondlifeURLcallback)(mHTML) ) + if ( (sSecondlifeURLcallback!=NULL) && !(*sSecondlifeURLcallback)(mHTML) ) { - if (mURLcallback!=NULL) (*mURLcallback)(mHTML); + if (sURLcallback!=NULL) (*sURLcallback)(mHTML); } mHTML.clear(); } @@ -4063,44 +4193,37 @@ BOOL LLTextEditor::handleMouseUpOverSegment(S32 x, S32 y, MASK mask) // Finds the text segment (if any) at the give local screen position -const LLTextSegment* LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) const +LLTextSegmentPtr LLTextEditor::getSegmentAtLocalPos( S32 x, S32 y ) { // Find the cursor position at the requested local screen position - S32 offset = getCursorPosFromLocalCoord( x, y, FALSE ); - S32 idx = getSegmentIdxAtOffset(offset); - return idx >= 0 ? mSegments[idx] : NULL; -} - -const LLTextSegment* LLTextEditor::getSegmentAtOffset(S32 offset) const -{ - S32 idx = getSegmentIdxAtOffset(offset); - return idx >= 0 ? mSegments[idx] : NULL; -} - -S32 LLTextEditor::getSegmentIdxAtOffset(S32 offset) const -{ - if (mSegments.empty() || offset < 0 || offset >= getLength()) + S32 offset = getDocIndexFromLocalCoord( x, y, FALSE ); + segment_set_t::iterator seg_iter = getSegIterContaining(offset); + if (seg_iter != mSegments.end()) { - return -1; + return *seg_iter; } else { - S32 segidx, segoff; - getSegmentAndOffset(offset, &segidx, &segoff); - return segidx; + return LLTextSegmentPtr(); } } -void LLTextEditor::onMouseCaptureLost() +LLTextEditor::segment_set_t::iterator LLTextEditor::getSegIterContaining(S32 index) { - endSelection(); + segment_set_t::iterator it = mSegments.upper_bound(new LLIndexSegment(index)); + return it; +} + +LLTextEditor::segment_set_t::const_iterator LLTextEditor::getSegIterContaining(S32 index) const +{ + LLTextEditor::segment_set_t::const_iterator it = mSegments.upper_bound(new LLIndexSegment(index)); + return it; } -void LLTextEditor::setOnScrollEndCallback(void (*callback)(void*), void* userdata) + +void LLTextEditor::onMouseCaptureLost() { - mOnScrollEndCallback = callback; - mOnScrollEndData = userdata; - mScrollbar->setOnScrollEndCallback(callback, userdata); + endSelection(); } /////////////////////////////////////////////////////////////////// @@ -4186,7 +4309,7 @@ BOOL LLTextEditor::importBuffer(const char* buffer, S32 length ) delete[] text; - setCursorPos(0); + startOfDoc(); deselect(); needsReflow(); @@ -4207,74 +4330,6 @@ BOOL LLTextEditor::exportBuffer(std::string &buffer ) return TRUE; } -////////////////////////////////////////////////////////////////////////// -// LLTextSegment - -LLTextSegment::LLTextSegment(S32 start) : - mStart(start), - mEnd(0), - mToken(NULL), - mIsDefault(FALSE) -{ -} -LLTextSegment::LLTextSegment( const LLStyleSP& style, S32 start, S32 end ) : - mStyle( style ), - mStart( start), - mEnd( end ), - mToken(NULL), - mIsDefault(FALSE) -{ -} -LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible) : - mStyle(new LLStyle(is_visible,color,LLStringUtil::null)), - mStart( start), - mEnd( end ), - mToken(NULL), - mIsDefault(FALSE) -{ -} -LLTextSegment::LLTextSegment( const LLColor4& color, S32 start, S32 end ) : - mStyle(new LLStyle(TRUE, color,LLStringUtil::null )), - mStart( start), - mEnd( end ), - mToken(NULL), - mIsDefault(FALSE) -{ -} -LLTextSegment::LLTextSegment( const LLColor3& color, S32 start, S32 end ) : - mStyle(new LLStyle(TRUE, color,LLStringUtil::null )), - mStart( start), - mEnd( end ), - mToken(NULL), - mIsDefault(FALSE) -{ -} - -BOOL LLTextSegment::getToolTip(std::string& msg) const -{ - if (mToken && !mToken->getToolTip().empty()) - { - const LLWString& wmsg = mToken->getToolTip(); - msg = wstring_to_utf8str(wmsg); - return TRUE; - } - return FALSE; -} - - - -void LLTextSegment::dump() const -{ - llinfos << "Segment [" << -// mColor.mV[VX] << ", " << -// mColor.mV[VY] << ", " << -// mColor.mV[VZ] << "]\t[" << - mStart << ", " << - getEnd() << "]" << - llendl; - -} - /////////////////////////////////////////////////////////////////// // Refactoring note: We may eventually want to replace this with boost::regex or // boost::tokenizer capabilities since we've already fixed at least two JIRAs @@ -4473,7 +4528,7 @@ void LLTextEditor::resetPreedit() deselect(); } - mCursorPos = mPreeditPositions.front(); + setCursorPos(mPreeditPositions.front()); removeStringNoUndo(mCursorPos, mPreeditPositions.back() - mCursorPos); insertStringNoUndo(mCursorPos, mPreeditOverwrittenWString); @@ -4557,7 +4612,7 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect return FALSE; } - const S32 first_visible_line = mScrollbar->getDocPos(); + const S32 first_visible_line = getFirstVisibleLine(); if (query < getLineStart(first_visible_line)) { return FALSE; @@ -4583,11 +4638,11 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect const LLWString textString(getWText()); const llwchar * const text = textString.c_str(); - const S32 line_height = llround(mGLFont->getLineHeight()); + const S32 line_height = llround(mDefaultFont->getLineHeight()); if (coord) { - const S32 query_x = mTextRect.mLeft + mGLFont->getWidth(text, current_line_start, query - current_line_start, mAllowEmbeddedItems); + const S32 query_x = mTextRect.mLeft + mDefaultFont->getWidth(text, current_line_start, query - current_line_start); const S32 query_y = mTextRect.mTop - (current_line - first_visible_line) * line_height - line_height / 2; S32 query_screen_x, query_screen_y; localPointToScreen(query_x, query_y, &query_screen_x, &query_screen_y); @@ -4599,17 +4654,17 @@ BOOL LLTextEditor::getPreeditLocation(S32 query_offset, LLCoordGL *coord, LLRect S32 preedit_left = mTextRect.mLeft; if (preedit_left_position > current_line_start) { - preedit_left += mGLFont->getWidth(text, current_line_start, preedit_left_position - current_line_start, mAllowEmbeddedItems); + preedit_left += mDefaultFont->getWidth(text, current_line_start, preedit_left_position - current_line_start); } S32 preedit_right = mTextRect.mLeft; if (preedit_right_position < current_line_end) { - preedit_right += mGLFont->getWidth(text, current_line_start, preedit_right_position - current_line_start, mAllowEmbeddedItems); + preedit_right += mDefaultFont->getWidth(text, current_line_start, preedit_right_position - current_line_start); } else { - preedit_right += mGLFont->getWidth(text, current_line_start, current_line_end - current_line_start, mAllowEmbeddedItems); + preedit_right += mDefaultFont->getWidth(text, current_line_start, current_line_end - current_line_start); } const S32 preedit_top = mTextRect.mTop - (current_line - first_visible_line) * line_height; @@ -4686,10 +4741,267 @@ void LLTextEditor::markAsPreedit(S32 position, S32 length) S32 LLTextEditor::getPreeditFontSize() const { - return llround(mGLFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); + return llround(mDefaultFont->getLineHeight() * LLUI::sGLScaleFactor.mV[VY]); } LLWString LLTextEditor::getWText() const { return getViewModel()->getDisplay(); } + +void LLTextEditor::onValueChange(S32 start, S32 end) +{ +} + +// +// LLTextSegment +// + +LLTextSegment::~LLTextSegment() +{} + +S32 LLTextSegment::getWidth(S32 first_char, S32 num_chars) const { return 0; } +S32 LLTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const { return 0; } +S32 LLTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const { return 0; } +void LLTextSegment::updateLayout(const LLTextEditor& editor) {} +F32 LLTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) { return draw_rect.mLeft; } +S32 LLTextSegment::getMaxHeight() const { return 0; } +bool LLTextSegment::canEdit() const { return false; } +void LLTextSegment::unlinkFromDocument(LLTextEditor*) {} +void LLTextSegment::linkToDocument(LLTextEditor*) {} +void LLTextSegment::setHasMouseHover(bool hover) {} +const LLColor4& LLTextSegment::getColor() const { return LLColor4::white; } +void LLTextSegment::setColor(const LLColor4 &color) {} +const LLStyleSP LLTextSegment::getStyle() const {static LLStyleSP sp(new LLStyle()); return sp; } +void LLTextSegment::setStyle(const LLStyleSP &style) {} +void LLTextSegment::setToken( LLKeywordToken* token ) {} +LLKeywordToken* LLTextSegment::getToken() const { return NULL; } +BOOL LLTextSegment::getToolTip( std::string& msg ) const { return FALSE; } +void LLTextSegment::dump() const {} + + +// +// LLNormalTextSegment +// + +LLNormalTextSegment::LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 end, LLTextEditor& editor ) +: LLTextSegment(start, end), + mStyle( style ), + mToken(NULL), + mHasMouseHover(false), + mEditor(editor) +{ + mMaxHeight = llceil(mStyle->getFont()->getLineHeight()); +} + +LLNormalTextSegment::LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextEditor& editor, BOOL is_visible) +: LLTextSegment(start, end), + mToken(NULL), + mHasMouseHover(false), + mEditor(editor) +{ + mStyle = new LLStyle(LLStyle::Params().visible(is_visible).color(color)); + + mMaxHeight = llceil(mStyle->getFont()->getLineHeight()); +} + +F32 LLNormalTextSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) +{ + if( end - start > 0 ) + { + if ( mStyle->isImage() && (start >= 0) && (end <= mEnd - mStart)) + { + S32 style_image_height = mStyle->mImageHeight; + S32 style_image_width = mStyle->mImageWidth; + LLUIImagePtr image = mStyle->getImage(); + image->draw(draw_rect.mLeft, draw_rect.mTop-style_image_height, + style_image_width, style_image_height); + } + + return drawClippedSegment( getStart() + start, getStart() + end, selection_start, selection_end, draw_rect.mLeft, draw_rect.mBottom); + } + return draw_rect.mLeft; +} + +// Draws a single text segment, reversing the color for selection if needed. +F32 LLNormalTextSegment::drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, F32 x, F32 y) +{ + const LLWString &text = mEditor.getWText(); + + F32 right_x = x; + if (!mStyle->isVisible()) + { + return right_x; + } + + const LLFontGL* font = mStyle->getFont(); + + LLColor4 color = mStyle->getColor(); + + font = mStyle->getFont(); + + if( selection_start > seg_start ) + { + // Draw normally + S32 start = seg_start; + S32 end = llmin( selection_start, seg_end ); + S32 length = end - start; + font->render(text, start, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems()); + } + x = right_x; + + if( (selection_start < seg_end) && (selection_end > seg_start) ) + { + // Draw reversed + S32 start = llmax( selection_start, seg_start ); + S32 end = llmin( selection_end, seg_end ); + S32 length = end - start; + + font->render(text, start, x, y, + LLColor4( 1.f - color.mV[0], 1.f - color.mV[1], 1.f - color.mV[2], 1.f ), + LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems()); + } + x = right_x; + if( selection_end < seg_end ) + { + // Draw normally + S32 start = llmax( selection_end, seg_start ); + S32 end = seg_end; + S32 length = end - start; + font->render(text, start, x, y, color, LLFontGL::LEFT, LLFontGL::BOTTOM, 0, LLFontGL::NO_SHADOW, length, S32_MAX, &right_x, mEditor.allowsEmbeddedItems()); + } + return right_x; +} + +S32 LLNormalTextSegment::getMaxHeight() const +{ + return mMaxHeight; +} + +BOOL LLNormalTextSegment::getToolTip(std::string& msg) const +{ + if (mToken && !mToken->getToolTip().empty()) + { + const LLWString& wmsg = mToken->getToolTip(); + msg = wstring_to_utf8str(wmsg); + return TRUE; + } + return FALSE; +} + + +S32 LLNormalTextSegment::getWidth(S32 first_char, S32 num_chars) const +{ + LLWString text = mEditor.getWText(); + return mStyle->getFont()->getWidth(text.c_str(), mStart + first_char, num_chars); +} + +S32 LLNormalTextSegment::getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const +{ + LLWString text = mEditor.getWText(); + return mStyle->getFont()->charFromPixelOffset(text.c_str(), mStart + start_offset, + (F32)segment_local_x_coord, + F32_MAX, + num_chars, + round); +} + +S32 LLNormalTextSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const +{ + LLWString text = mEditor.getWText(); + S32 num_chars = mStyle->getFont()->maxDrawableChars(text.c_str() + segment_offset + mStart, + (F32)num_pixels, + max_chars, + mEditor.getWordWrap()); + + if (num_chars == 0 + && line_offset == 0 + && max_chars > 0) + { + // If at the beginning of a line, and a single character won't fit, draw it anyway + num_chars = 1; + } + if (mStart + segment_offset + num_chars == mEditor.getLength()) + { + // include terminating NULL + num_chars++; + } + return num_chars; +} + +void LLNormalTextSegment::dump() const +{ + llinfos << "Segment [" << +// mColor.mV[VX] << ", " << +// mColor.mV[VY] << ", " << +// mColor.mV[VZ] << "]\t[" << + mStart << ", " << + getEnd() << "]" << + llendl; +} + +// +// LLInlineViewSegment +// + +LLInlineViewSegment::LLInlineViewSegment(LLView* view, S32 start, S32 end) +: LLTextSegment(start, end), + mView(view) +{ +} + +LLInlineViewSegment::~LLInlineViewSegment() +{ + mView->die(); +} + +S32 LLInlineViewSegment::getWidth(S32 first_char, S32 num_chars) const +{ + if (first_char == 0 && num_chars == 0) + { + return 0; + } + else + { + return mView->getRect().getWidth(); + } +} + +S32 LLInlineViewSegment::getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const +{ + if (line_offset != 0 && num_pixels < mView->getRect().getWidth()) + { + return 0; + } + else + { + return mEnd - mStart; + } +} + +void LLInlineViewSegment::updateLayout(const LLTextEditor& editor) +{ + LLRect start_rect = editor.getLocalRectFromDocIndex(mStart); + LLRect doc_rect = editor.getDocumentPanel()->getRect(); + mView->setOrigin(doc_rect.mLeft + start_rect.mLeft, doc_rect.mBottom + start_rect.mBottom); +} + +F32 LLInlineViewSegment::draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) +{ + return (F32)(draw_rect.mLeft + mView->getRect().getWidth()); +} + +S32 LLInlineViewSegment::getMaxHeight() const +{ + return mView->getRect().getHeight(); +} + +void LLInlineViewSegment::unlinkFromDocument(LLTextEditor* editor) +{ + editor->removeDocumentChild(mView); +} + +void LLInlineViewSegment::linkToDocument(LLTextEditor* editor) +{ + editor->addDocumentChild(mView); +} diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h index 0babd7ba58..67c67d0f67 100644 --- a/indra/llui/lltexteditor.h +++ b/indra/llui/lltexteditor.h @@ -53,6 +53,101 @@ class LLScrollbar; class LLKeywordToken; class LLTextCmd; class LLUICtrlFactory; +class LLScrollContainer; + +class LLTextSegment : public LLRefCount +{ +public: + LLTextSegment(S32 start, S32 end) : mStart(start), mEnd(end){}; + virtual ~LLTextSegment(); + + virtual S32 getWidth(S32 first_char, S32 num_chars) const; + virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; + virtual S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; + virtual void updateLayout(const class LLTextEditor& editor); + virtual F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + virtual S32 getMaxHeight() const; + virtual bool canEdit() const; + virtual void unlinkFromDocument(class LLTextEditor* editor); + virtual void linkToDocument(class LLTextEditor* editor); + + virtual void setHasMouseHover(bool hover); + virtual const LLColor4& getColor() const; + virtual void setColor(const LLColor4 &color); + virtual const LLStyleSP getStyle() const; + virtual void setStyle(const LLStyleSP &style); + virtual void setToken( LLKeywordToken* token ); + virtual LLKeywordToken* getToken() const; + virtual BOOL getToolTip( std::string& msg ) const; + virtual void dump() const; + + S32 getStart() const { return mStart; } + void setStart(S32 start) { mStart = start; } + S32 getEnd() const { return mEnd; } + void setEnd( S32 end ) { mEnd = end; } + +protected: + S32 mStart; + S32 mEnd; +}; + +class LLNormalTextSegment : public LLTextSegment +{ +public: + LLNormalTextSegment( const LLStyleSP& style, S32 start, S32 end, LLTextEditor& editor ); + LLNormalTextSegment( const LLColor4& color, S32 start, S32 end, LLTextEditor& editor, BOOL is_visible = TRUE); + + /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const; + /*virtual*/ S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; + /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + /*virtual*/ S32 getMaxHeight() const; + /*virtual*/ bool canEdit() const { return true; } + /*virtual*/ void setHasMouseHover(bool hover) { mHasMouseHover = hover; } + /*virtual*/ const LLColor4& getColor() const { return mStyle->getColor(); } + /*virtual*/ void setColor(const LLColor4 &color) { mStyle->setColor(color); } + /*virtual*/ const LLStyleSP getStyle() const { return mStyle; } + /*virtual*/ void setStyle(const LLStyleSP &style) { mStyle = style; } + /*virtual*/ void setToken( LLKeywordToken* token ) { mToken = token; } + /*virtual*/ LLKeywordToken* getToken() const { return mToken; } + /*virtual*/ BOOL getToolTip( std::string& msg ) const; + /*virtual*/ void dump() const; + +protected: + F32 drawClippedSegment(S32 seg_start, S32 seg_end, S32 selection_start, S32 selection_end, F32 x, F32 y); + + class LLTextEditor& mEditor; + LLStyleSP mStyle; + S32 mMaxHeight; + LLKeywordToken* mToken; + bool mHasMouseHover; +}; + +typedef LLPointer<LLTextSegment> LLTextSegmentPtr; + +class LLInlineViewSegment : public LLTextSegment +{ +public: + LLInlineViewSegment(LLView* widget, S32 start, S32 end); + ~LLInlineViewSegment(); + /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const; + /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const; + /*virtual*/ void updateLayout(const class LLTextEditor& editor); + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect); + /*virtuaL*/ S32 getMaxHeight() const; + /*virtual*/ bool canEdit() const { return false; } + /*virtual*/ void unlinkFromDocument(class LLTextEditor* editor); + /*virtual*/ void linkToDocument(class LLTextEditor* editor); + +private: + LLView* mView; +}; + +class LLIndexSegment : public LLTextSegment +{ +public: + LLIndexSegment(S32 pos) : LLTextSegment(pos, pos) {} +}; class LLTextEditor : public LLUICtrl, LLEditMenuHandler, protected LLPreeditor { @@ -64,12 +159,13 @@ public: Optional<bool> read_only, embedded_items, - hide_scrollbar, word_wrap, ignore_tab, hide_border, track_bottom, - takes_non_scroll_clicks; + handle_edit_keys_directly, + show_line_numbers, + commit_on_focus_lost; //colors Optional<LLUIColor> cursor_color, @@ -78,13 +174,15 @@ public: text_readonly_color, bg_readonly_color, bg_writeable_color, - bg_focus_color; + bg_focus_color, + link_color; Optional<LLViewBorder::Params> border; Ignored type, length, - is_unicode; + is_unicode, + hide_scrollbar; Params(); }; @@ -101,6 +199,17 @@ public: static const llwchar LAST_EMBEDDED_CHAR = 0x10ffff; static const S32 MAX_EMBEDDED_ITEMS = LAST_EMBEDDED_CHAR - FIRST_EMBEDDED_CHAR + 1; + + struct compare_segment_end + { + bool operator()(const LLTextSegmentPtr& a, const LLTextSegmentPtr& b) const + { + return a->getEnd() < b->getEnd(); + } + }; + + typedef std::multiset<LLTextSegmentPtr, compare_segment_end> segment_set_t; + virtual ~LLTextEditor(); void setParseHTML(BOOL parsing) {mParseHTML=parsing;} @@ -110,7 +219,6 @@ public: virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); - virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask ); virtual BOOL handleMiddleMouseDown(S32 x,S32 y,MASK mask); @@ -128,14 +236,15 @@ public: virtual void draw(); virtual void onFocusReceived(); virtual void onFocusLost(); + virtual void onCommit(); virtual void setEnabled(BOOL enabled); // uictrl overrides - virtual void onTabInto(); virtual void clear(); virtual void setFocus( BOOL b ); virtual BOOL acceptsTextInput() const; - virtual BOOL isDirty() const { return( mLastCmd != NULL || (mPristineCmd && (mPristineCmd != mLastCmd)) ); } + virtual BOOL isDirty() const { return isPristine(); } + virtual void setValue(const LLSD& value); // LLEditMenuHandler interface virtual void undo(); @@ -162,9 +271,12 @@ public: virtual void deselect(); virtual BOOL canDeselect() const; + virtual void onValueChange(S32 start, S32 end); + void selectNext(const std::string& search_text_in, BOOL case_insensitive, BOOL wrap = TRUE); BOOL replaceText(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive, BOOL wrap = TRUE); void replaceTextAll(const std::string& search_text, const std::string& replace_text, BOOL case_insensitive); + BOOL hasSelection() const { return (mSelectionStart !=mSelectionEnd); } // Undo/redo stack void blockUndo(); @@ -173,12 +285,20 @@ public: virtual void makePristine(); BOOL isPristine() const; BOOL allowsEmbeddedItems() const { return mAllowEmbeddedItems; } + BOOL getWordWrap() { return mWordWrap; } + S32 getLength() const { return getWText().length(); } + void setReadOnly(bool read_only) { mReadOnly = read_only; } + bool getReadOnly() { return mReadOnly; } + + // + // Text manipulation + // // inserts text at cursor void insertText(const std::string &text); // appends text at end void appendText(const std::string &wtext, bool allow_undo, bool prepend_newline, - const LLStyleSP stylep = NULL); + const LLStyle::Params& style = LLStyle::Params()); void appendColoredText(const std::string &wtext, bool allow_undo, bool prepend_newline, @@ -187,19 +307,24 @@ public: // if styled text starts a line, you need to prepend a newline. void appendStyledText(const std::string &new_text, bool allow_undo, bool prepend_newline, - LLStyleSP stylep = NULL); + const LLStyle::Params& style); void appendHighlightedText(const std::string &new_text, bool allow_undo, bool prepend_newline, S32 highlight_part, - LLStyleSP stylep); - + const LLStyle::Params& style); + void appendWidget(LLView* widget, const std::string &widget_text, bool allow_undo, bool prepend_newline); + // Non-undoable + void setText(const LLStringExplicit &utf8str); + void setWText(const LLWString &wtext); + + // Removes text from the end of document // Does not change highlight or cursor position. void removeTextFromEnd(S32 num_chars); BOOL tryToRevertToPristineState(); - void setCursor(S32 row, S32 column); - void setCursorPos(S32 offset); + bool setCursor(S32 row, S32 column); + bool setCursorPos(S32 offset, bool keep_cursor_offset = false); void setCursorAndScrollToEnd(); void getLineAndColumnForPosition( S32 position, S32* line, S32* col, BOOL include_wordwrap ); @@ -214,90 +339,57 @@ public: LLKeywords::keyword_iterator_t keywordsBegin() { return mKeywords.begin(); } LLKeywords::keyword_iterator_t keywordsEnd() { return mKeywords.end(); } - // Color support - void setCursorColor(const LLColor4& c) { mCursorColor = c; } - void setFgColor( const LLColor4& c ) { mFgColor = c; } - void setTextDefaultColor( const LLColor4& c ) { mDefaultColor = c; } - void setReadOnlyFgColor( const LLColor4& c ) { mReadOnlyFgColor = c; } - void setWriteableBgColor( const LLColor4& c ) { mWriteableBgColor = c; } - void setReadOnlyBgColor( const LLColor4& c ) { mReadOnlyBgColor = c; } - void setTrackColor( const LLColor4& color ); - void setThumbColor( const LLColor4& color ); - // Hacky methods to make it into a word-wrapping, potentially scrolling, // read-only text box. - void setBorderVisible(BOOL b); - BOOL isBorderVisible() const; - void setTakesNonScrollClicks(BOOL b) { mTakesNonScrollClicks = b; } - void setHideScrollbarForShortDocs(BOOL b); - - void setWordWrap( BOOL b ); - void setTabsToNextField(BOOL b) { mTabsToNextField = b; } - BOOL tabsToNextField() const { return mTabsToNextField; } void setCommitOnFocusLost(BOOL b) { mCommitOnFocusLost = b; } // Hack to handle Notecards virtual BOOL importBuffer(const char* buffer, S32 length ); virtual BOOL exportBuffer(std::string& buffer ); - // If takes focus, will take keyboard focus on click. - void setTakesFocus(BOOL b) { mTakesFocus = b; } + const class DocumentPanel* getDocumentPanel() const { return mDocumentPanel; } - void setSourceID(const LLUUID& id) { mSourceID = id; } const LLUUID& getSourceID() const { return mSourceID; } - void setHandleEditKeysDirectly( BOOL b ) { mHandleEditKeysDirectly = b; } - // Callbacks - static void setLinkColor(LLColor4 color) { mLinkColor = color; } static void setURLCallbacks(void (*callback1) (const std::string& url), bool (*callback2) (const std::string& url), bool (*callback3) (const std::string& url) ) - { mURLcallback = callback1; mSecondlifeURLcallback = callback2; mSecondlifeURLcallbackRightClick = callback3;} - - void setOnScrollEndCallback(void (*callback)(void*), void* userdata); - - // new methods - void setValue(const LLSD& value); + { sURLcallback = callback1; sSecondlifeURLcallback = callback2; sSecondlifeURLcallbackRightClick = callback3;} std::string getText() const; - // Non-undoable - void setText(const LLStringExplicit &utf8str); - void setWText(const LLWString &wtext); - - // Returns byte length limit - S32 getMaxLength() const { return mMaxTextByteLength; } - - // Change cursor - void startOfLine(); - void endOfLine(); - void endOfDoc(); - - BOOL isScrolledToTop(); - BOOL isScrolledToBottom(); - // Getters LLWString getWText() const; llwchar getWChar(S32 pos) const { return getWText()[pos]; } LLWString getWSubString(S32 pos, S32 len) const { return getWText().substr(pos, len); } - const LLTextSegment* getCurrentSegment() const { return getSegmentAtOffset(mCursorPos); } - const LLTextSegment* getPreviousSegment() const; - void getSelectedSegments(std::vector<const LLTextSegment*>& segments) const; + typedef std::vector<LLTextSegmentPtr> segment_vec_t; + + const LLTextSegmentPtr getPreviousSegment() const; + void getSelectedSegments(segment_vec_t& segments) const; + + void getSegmentsInRange(segment_vec_t& segments, S32 start, S32 end, bool include_partial) const; + LLRect getLocalRectFromDocIndex(S32 index) const; - static bool isPartOfWord(llwchar c) { return (c == '_') || LLStringOps::isAlnum((char)c); } + void addDocumentChild(LLView* view); + void removeDocumentChild(LLView* view); protected: - // - // Methods - // + // Change cursor + void startOfLine(); + void endOfLine(); + void startOfDoc(); + void endOfDoc(); - S32 getLength() const { return getWText().length(); } - void getSegmentAndOffset( S32 startpos, S32* segidxp, S32* offsetp ) const; + void getSegmentAndOffset( S32 startpos, segment_set_t::const_iterator* seg_iter, S32* offsetp ) const; + void getSegmentAndOffset( S32 startpos, segment_set_t::iterator* seg_iter, S32* offsetp ) ; void drawPreeditMarker(); - void updateLineStartList(S32 startpos = 0); + void needsReflow() { mReflowNeeded = TRUE; } + void needsScroll() { mScrollNeeded = TRUE; } + void updateCursorXPos(); + void updateScrollFromCursor(); void updateTextRect(); const LLRect& getTextRect() const { return mTextRect; } @@ -306,16 +398,16 @@ protected: BOOL truncate(); // Returns true if truncation occurs void removeCharOrTab(); - void setCursorAtLocalPos(S32 x, S32 y, BOOL round); - S32 getCursorPosFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const; + void setCursorAtLocalPos(S32 x, S32 y, bool round, bool keep_cursor_offset = false); + S32 getDocIndexFromLocalCoord( S32 local_x, S32 local_y, BOOL round ) const; void indentSelectedLines( S32 spaces ); S32 indentLine( S32 pos, S32 spaces ); void unindentLineBeforeCloseBrace(); - S32 getSegmentIdxAtOffset(S32 offset) const; - const LLTextSegment* getSegmentAtLocalPos(S32 x, S32 y) const; - const LLTextSegment* getSegmentAtOffset(S32 offset) const; + LLTextSegmentPtr getSegmentAtLocalPos(S32 x, S32 y); + segment_set_t::iterator getSegIterContaining(S32 index); + segment_set_t::const_iterator getSegIterContaining(S32 index) const; void reportBadKeystroke() { make_ui_sound("UISndBadKeystroke"); } @@ -325,7 +417,6 @@ protected: BOOL handleControlKey(const KEY key, const MASK mask); BOOL handleEditKey(const KEY key, const MASK mask); - BOOL hasSelection() const { return (mSelectionStart !=mSelectionEnd); } BOOL selectionContainsLineBreaks(); void startSelection(); void endSelection(); @@ -334,9 +425,10 @@ protected: S32 prevWordPos(S32 cursorPos) const; S32 nextWordPos(S32 cursorPos) const; - S32 getLineCount() const { return mLineStartList.size(); } + S32 getLineCount() const { return mLineInfoList.size(); } S32 getLineStart( S32 line ) const; - void getLineAndOffset(S32 pos, S32* linep, S32* offsetp) const; + S32 getLineHeight( S32 line ) const; + void getLineAndOffset(S32 pos, S32* linep, S32* offsetp, bool include_wordwrap = true) const; S32 getPos(S32 line, S32 offset); void changePage(S32 delta); @@ -344,13 +436,13 @@ protected: void autoIndent(); - void findEmbeddedItemSegments(); + void findEmbeddedItemSegments(S32 start, S32 end); + void insertSegment(LLTextSegmentPtr segment_to_insert); + virtual BOOL handleMouseUpOverSegment(S32 x, S32 y, MASK mask); virtual llwchar pasteEmbeddedItem(llwchar ext_char) { return ext_char; } - virtual void bindEmbeddedChars(const LLFontGL* font) const {} - virtual void unbindEmbeddedChars(const LLFontGL* font) const {} S32 findHTMLToken(const std::string &line, S32 pos, BOOL reverse) const; BOOL findHTML(const std::string &line, S32 *begin, S32 *end) const; @@ -361,7 +453,15 @@ protected: class LLTextCmd { public: - LLTextCmd( S32 pos, BOOL group_with_next ) : mPos(pos), mGroupWithNext(group_with_next) {} + LLTextCmd( S32 pos, BOOL group_with_next, LLTextSegmentPtr segment = LLTextSegmentPtr() ) + : mPos(pos), + mGroupWithNext(group_with_next) + { + if (segment.notNull()) + { + mSegments.push_back(segment); + } + } virtual ~LLTextCmd() {} virtual BOOL execute(LLTextEditor* editor, S32* delta) = 0; virtual S32 undo(LLTextEditor* editor) = 0; @@ -372,16 +472,17 @@ protected: virtual BOOL hasExtCharValue( llwchar value ) const { return FALSE; } // Defined here so they can access protected LLTextEditor editing methods - S32 insert(LLTextEditor* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr ); } + S32 insert(LLTextEditor* editor, S32 pos, const LLWString &wstr) { return editor->insertStringNoUndo( pos, wstr, &mSegments ); } S32 remove(LLTextEditor* editor, S32 pos, S32 length) { return editor->removeStringNoUndo( pos, length ); } S32 overwrite(LLTextEditor* editor, S32 pos, llwchar wc) { return editor->overwriteCharNoUndo(pos, wc); } S32 getPosition() const { return mPos; } BOOL groupWithNext() const { return mGroupWithNext; } - private: - const S32 mPos; - BOOL mGroupWithNext; + protected: + const S32 mPos; + BOOL mGroupWithNext; + segment_vec_t mSegments; }; // Here's the method that takes and applies text commands. S32 execute(LLTextCmd* cmd); @@ -392,12 +493,12 @@ protected: S32 overwriteChar(S32 pos, llwchar wc); void removeChar(); S32 removeChar(S32 pos); - S32 insert(const S32 pos, const LLWString &wstr, const BOOL group_with_next_op); - S32 remove(const S32 pos, const S32 length, const BOOL group_with_next_op); - S32 append(const LLWString &wstr, const BOOL group_with_next_op); + S32 insert(S32 pos, const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment); + S32 remove(S32 pos, S32 length, bool group_with_next_op); + S32 append(const LLWString &wstr, bool group_with_next_op, LLTextSegmentPtr segment); // Direct operations - S32 insertStringNoUndo(S32 pos, const LLWString &wstr); // returns num of chars actually inserted + S32 insertStringNoUndo(S32 pos, const LLWString &wstr, segment_vec_t* segments = NULL); // returns num of chars actually inserted S32 removeStringNoUndo(S32 pos, S32 length); S32 overwriteCharNoUndo(S32 pos, llwchar wc); @@ -441,22 +542,38 @@ protected: BOOL mParseHighlights; std::string mHTML; - typedef std::vector<LLTextSegment *> segment_list_t; - segment_list_t mSegments; - const LLTextSegment* mHoverSegment; + segment_set_t mSegments; + LLTextSegmentPtr mHoverSegment; // Scrollbar data - class LLScrollbar* mScrollbar; - BOOL mHideScrollbarForShortDocs; - BOOL mTakesNonScrollClicks; - void (*mOnScrollEndCallback)(void*); + class DocumentPanel* mDocumentPanel; + LLScrollContainer* mScroller; + void *mOnScrollEndData; LLWString mPreeditWString; LLWString mPreeditOverwrittenWString; std::vector<S32> mPreeditPositions; std::vector<BOOL> mPreeditStandouts; - + + S32 mScrollIndex; // index into document that controls default scroll position + +protected: + LLUIColor mCursorColor; + LLUIColor mFgColor; + LLUIColor mDefaultColor; + LLUIColor mReadOnlyFgColor; + LLUIColor mWriteableBgColor; + LLUIColor mReadOnlyBgColor; + LLUIColor mFocusBgColor; + LLUIColor mLinkColor; + + BOOL mReadOnly; + BOOL mWordWrap; + BOOL mShowLineNumbers; + + void updateSegments(); + private: // @@ -465,32 +582,28 @@ private: void pasteHelper(bool is_primary); virtual LLTextViewModel* getViewModel() const; - - void updateSegments(); - void pruneSegments(); + void reflow(S32 startpos = 0); + + void clearSegments(); + void createDefaultSegment(); + LLStyleSP getDefaultStyle(); + S32 getEditableIndex(S32 index, bool increasing_direction); void drawBackground(); void drawSelectionBackground(); void drawCursor(); void drawText(); - void drawClippedSegment(const LLWString &wtext, S32 seg_start, S32 seg_end, F32 x, F32 y, S32 selection_left, S32 selection_right, const LLStyleSP& color, F32* right_x); - - void needsReflow() - { - mReflowNeeded = TRUE; - // cursor might have moved, need to scroll - mScrollNeeded = TRUE; - } - void needsScroll() { mScrollNeeded = TRUE; } + void drawLineNumbers(); + + S32 getFirstVisibleLine() const; // // Data // LLKeywords mKeywords; - static LLUIColor mLinkColor; - static void (*mURLcallback) (const std::string& url); - static bool (*mSecondlifeURLcallback) (const std::string& url); - static bool (*mSecondlifeURLcallbackRightClick) (const std::string& url); + static void (*sURLcallback) (const std::string& url); + static bool (*sSecondlifeURLcallback) (const std::string& url); + static bool (*sSecondlifeURLcallbackRightClick) (const std::string& url); // Concrete LLTextCmd sub-classes used by the LLTextEditor base class class LLTextCmdInsert; @@ -500,7 +613,7 @@ private: S32 mMaxTextByteLength; // Maximum length mText is allowed to be in bytes - const LLFontGL* mGLFont; + const LLFontGL* mDefaultFont; class LLViewBorder* mBorder; @@ -515,49 +628,35 @@ private: S32 mDesiredXPixel; // X pixel position where the user wants the cursor to be LLRect mTextRect; // The rect in which text is drawn. Excludes borders. // List of offsets and segment index of the start of each line. Always has at least one node (0). - struct pred; struct line_info { - line_info(S32 segment, S32 offset) : mSegment(segment), mOffset(offset) {} - S32 mSegment; - S32 mOffset; - }; - struct line_info_compare - { - bool operator()(const line_info& a, const line_info& b) const - { - if (a.mSegment < b.mSegment) - return true; - else if (a.mSegment > b.mSegment) - return false; - else - return a.mOffset < b.mOffset; - } + line_info(S32 index_start, S32 index_end, S32 top, S32 bottom, S32 line_num) + : mDocIndexStart(index_start), + mDocIndexEnd(index_end), + mTop(top), + mBottom(bottom), + mLineNum(line_num) + {} + S32 mDocIndexStart; + S32 mDocIndexEnd; + S32 mTop; + S32 mBottom; + S32 mLineNum; // actual line count (ignoring soft newlines due to word wrap) }; + struct compare_bottom; + struct compare_top; + struct line_end_compare; typedef std::vector<line_info> line_list_t; - line_list_t mLineStartList; + line_list_t mLineInfoList; BOOL mReflowNeeded; BOOL mScrollNeeded; LLFrameTimer mKeystrokeTimer; - LLUIColor mCursorColor; - LLUIColor mFgColor; - LLUIColor mDefaultColor; - LLUIColor mReadOnlyFgColor; - LLUIColor mWriteableBgColor; - LLUIColor mReadOnlyBgColor; - LLUIColor mFocusBgColor; - - BOOL mReadOnly; - BOOL mWordWrap; - BOOL mShowLineNumbers; - BOOL mTabsToNextField; // if true, tab moves focus to next field, else inserts spaces BOOL mCommitOnFocusLost; BOOL mTakesFocus; BOOL mTrackBottom; // if true, keeps scroll position at bottom during resize - BOOL mScrolledToBottom; BOOL mAllowEmbeddedItems; @@ -571,47 +670,4 @@ private: }; // end class LLTextEditor - -class LLTextSegment -{ -public: - // for creating a compare value - LLTextSegment(S32 start); - LLTextSegment( const LLStyleSP& style, S32 start, S32 end ); - LLTextSegment( const LLColor4& color, S32 start, S32 end, BOOL is_visible); - LLTextSegment( const LLColor4& color, S32 start, S32 end ); - LLTextSegment( const LLColor3& color, S32 start, S32 end ); - - S32 getStart() const { return mStart; } - S32 getEnd() const { return mEnd; } - void setEnd( S32 end ) { mEnd = end; } - const LLColor4& getColor() const { return mStyle->getColor(); } - void setColor(const LLColor4 &color) { mStyle->setColor(color); } - const LLStyleSP& getStyle() const { return mStyle; } - void setStyle(const LLStyleSP &style) { mStyle = style; } - void setIsDefault(BOOL b) { mIsDefault = b; } - BOOL getIsDefault() const { return mIsDefault; } - void setToken( LLKeywordToken* token ) { mToken = token; } - LLKeywordToken* getToken() const { return mToken; } - BOOL getToolTip( std::string& msg ) const; - - void dump() const; - - struct compare - { - bool operator()(const LLTextSegment* a, const LLTextSegment* b) const - { - return a->mStart < b->mStart; - } - }; - -private: - LLStyleSP mStyle; - S32 mStart; - S32 mEnd; - LLKeywordToken* mToken; - BOOL mIsDefault; -}; - - #endif // LL_TEXTEDITOR_ 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/llui.cpp b/indra/llui/llui.cpp index fab8f61356..1d62ed93f9 100644 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -38,7 +38,6 @@ #include <map> // Linden library includes -#include "audioengine.h" #include "v2math.h" #include "v4color.h" #include "llrender.h" @@ -71,9 +70,6 @@ // const LLColor4 UI_VERTEX_COLOR(1.f, 1.f, 1.f, 1.f); -// Used to hide the flashing text cursor when window doesn't have focus. -BOOL gShowTextEditCursor = TRUE; - // Language for UI construction std::map<std::string, std::string> gTranslation; std::list<std::string> gUntranslated; @@ -1898,6 +1894,12 @@ void LLScreenClipRect::pushClipRect(const LLRect& rect) { LLRect top = sClipRectStack.top(); combined_clip_rect.intersectWith(top); + + if(combined_clip_rect.isEmpty()) + { + // avoid artifacts where zero area rects show up as lines + combined_clip_rect = LLRect::null; + } } sClipRectStack.push(combined_clip_rect); } diff --git a/indra/llui/llui.h b/indra/llui/llui.h index b1943a7b02..1f9b0b2dbc 100644 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -151,9 +151,6 @@ inline void gl_rect_2d_offset_local( const LLRect& rect, S32 pixel_offset, BOOL gl_rect_2d_offset_local( rect.mLeft, rect.mTop, rect.mRight, rect.mBottom, pixel_offset, filled ); } -// Used to hide the flashing text cursor when window doesn't have focus. -extern BOOL gShowTextEditCursor; - class LLImageProviderInterface; typedef void (*LLUIAudioCallback)(const LLUUID& uuid); diff --git a/indra/llui/lluictrl.cpp b/indra/llui/lluictrl.cpp index 8aa7540446..7ff942268d 100644 --- a/indra/llui/lluictrl.cpp +++ b/indra/llui/lluictrl.cpp @@ -54,61 +54,7 @@ LLUICtrl::Params::Params() addSynonym(initial_value, "initial_value"); } -LLFocusableElement::LLFocusableElement() -: mFocusLostCallback(NULL), - mFocusReceivedCallback(NULL), - mFocusChangedCallback(NULL), - mTopLostCallback(NULL), - mFocusCallbackUserData(NULL) -{ -} - -//virtual -LLFocusableElement::~LLFocusableElement() -{ -} - -void LLFocusableElement::onFocusReceived() -{ - if( mFocusReceivedCallback ) - { - mFocusReceivedCallback( this, mFocusCallbackUserData ); - } - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mFocusCallbackUserData ); - } -} - -void LLFocusableElement::onFocusLost() -{ - if( mFocusLostCallback ) - { - mFocusLostCallback( this, mFocusCallbackUserData ); - } - - if( mFocusChangedCallback ) - { - mFocusChangedCallback( this, mFocusCallbackUserData ); - } -} - -void LLFocusableElement::onTopLost() -{ - if (mTopLostCallback) - { - mTopLostCallback(this, mFocusCallbackUserData); - } -} - -BOOL LLFocusableElement::hasFocus() const -{ - return FALSE; -} - -void LLFocusableElement::setFocus(BOOL b) -{ -} +// NOTE: the LLFocusableElement implementation has been moved from here to llfocusmgr.cpp. //static const LLUICtrl::Params& LLUICtrl::getDefaultParams() @@ -278,24 +224,46 @@ void LLUICtrl::onMouseLeave(S32 x, S32 y, MASK mask) { mMouseLeaveSignal(this, getValue()); } + //virtual -BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask){ +BOOL LLUICtrl::handleMouseDown(S32 x, S32 y, MASK mask) +{ BOOL handled = LLView::handleMouseDown(x,y,mask); mMouseDownSignal(this,x,y,mask); return handled; } + //virtual -BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask){ +BOOL LLUICtrl::handleMouseUp(S32 x, S32 y, MASK mask) +{ BOOL handled = LLView::handleMouseUp(x,y,mask); mMouseUpSignal(this,x,y,mask); return handled; } + //virtual -BOOL LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask){ +BOOL LLUICtrl::handleRightMouseDown(S32 x, S32 y, MASK mask) +{ + BOOL handled = LLView::handleRightMouseDown(x,y,mask); + mRightMouseDownSignal(this,x,y,mask); + return handled; +} + +//virtual +BOOL LLUICtrl::handleRightMouseUp(S32 x, S32 y, MASK mask) +{ BOOL handled = LLView::handleRightMouseUp(x,y,mask); - mRightClickSignal(this,x,y,mask); + mRightMouseUpSignal(this,x,y,mask); return handled; } + +// can't tab to children of a non-tab-stop widget +BOOL LLUICtrl::canFocusChildren() const +{ + return hasTabStop(); +} + + void LLUICtrl::onCommit() { mCommitSignal(this, getValue()); @@ -538,7 +506,7 @@ void LLUICtrl::onFocusReceived() // find first view in hierarchy above new focus that is a LLUICtrl LLView* viewp = getParent(); - LLUICtrl* last_focus = gFocusMgr.getLastKeyboardFocus(); + LLUICtrl* last_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getLastKeyboardFocus()); while (viewp && !viewp->isCtrl()) { @@ -864,6 +832,9 @@ BOOL LLUICtrl::getTentative() const void LLUICtrl::setColor(const LLColor4& color) { } +// virtual +void LLUICtrl::setAlpha(F32 alpha) +{ } namespace LLInitParam diff --git a/indra/llui/lluictrl.h b/indra/llui/lluictrl.h index 6ba3b01fcb..3e2e1f41a1 100644 --- a/indra/llui/lluictrl.h +++ b/indra/llui/lluictrl.h @@ -46,35 +46,10 @@ const BOOL TAKE_FOCUS_YES = TRUE; const BOOL TAKE_FOCUS_NO = FALSE; -class LLFocusableElement -{ - friend class LLFocusMgr; // allow access to focus change handlers -public: - LLFocusableElement(); - virtual ~LLFocusableElement(); - - virtual void setFocus( BOOL b ); - virtual BOOL hasFocus() const; - - typedef boost::function<void(LLFocusableElement*, void*)> focus_callback_t; - void setFocusLostCallback(focus_callback_t cb, void* user_data = NULL) { mFocusLostCallback = cb; mFocusCallbackUserData = user_data; } - void setFocusReceivedCallback(focus_callback_t cb, void* user_data = NULL) { mFocusReceivedCallback = cb; mFocusCallbackUserData = user_data; } - void setFocusChangedCallback(focus_callback_t cb, void* user_data = NULL ) { mFocusChangedCallback = cb; mFocusCallbackUserData = user_data; } - void setTopLostCallback(focus_callback_t cb, void* user_data = NULL ) { mTopLostCallback = cb; mFocusCallbackUserData = user_data; } - -protected: - virtual void onFocusReceived(); - virtual void onFocusLost(); - virtual void onTopLost(); // called when registered as top ctrl and user clicks elsewhere - focus_callback_t mFocusLostCallback; - focus_callback_t mFocusReceivedCallback; - focus_callback_t mFocusChangedCallback; - focus_callback_t mTopLostCallback; - void* mFocusCallbackUserData; -}; +// NOTE: the LLFocusableElement class declaration has been moved from here to llfocusmgr.h. class LLUICtrl - : public LLView, public LLFocusableElement, public boost::signals2::trackable + : public LLView, public boost::signals2::trackable { public: @@ -186,8 +161,10 @@ public: /*virtual*/ BOOL getTentative() const; /*virtual*/ void onMouseEnter(S32 x, S32 y, MASK mask); /*virtual*/ void onMouseLeave(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL canFocusChildren() const; /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); // From LLFocusableElement @@ -235,6 +212,7 @@ public: virtual void onTabInto(); virtual void clear(); virtual void setColor(const LLColor4& color); + virtual void setAlpha(F32 alpha); BOOL focusNextItem(BOOL text_entry_only); BOOL focusPrevItem(BOOL text_entry_only); @@ -258,12 +236,13 @@ public: boost::signals2::connection setMouseDownCallback( const mouse_signal_t::slot_type& cb ) { return mMouseDownSignal.connect(cb); } boost::signals2::connection setMouseUpCallback( const mouse_signal_t::slot_type& cb ) { return mMouseUpSignal.connect(cb); } - boost::signals2::connection setRightClickedCallback( const mouse_signal_t::slot_type& cb ) { return mRightClickSignal.connect(cb); } + boost::signals2::connection setRightMouseDownCallback( const mouse_signal_t::slot_type& cb ) { return mRightMouseDownSignal.connect(cb); } + boost::signals2::connection setRightMouseUpCallback( const mouse_signal_t::slot_type& cb ) { return mRightMouseUpSignal.connect(cb); } // *TODO: Deprecate; for backwards compatability only: boost::signals2::connection setCommitCallback( boost::function<void (LLUICtrl*,void*)> cb, void* data); boost::signals2::connection setValidateBeforeCommit( boost::function<bool (const LLSD& data)> cb ); - + LLUICtrl* findRootMostFocusRoot(); class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter> @@ -292,7 +271,8 @@ protected: mouse_signal_t mMouseDownSignal; mouse_signal_t mMouseUpSignal; - mouse_signal_t mRightClickSignal; + mouse_signal_t mRightMouseDownSignal; + mouse_signal_t mRightMouseUpSignal; LLViewModelPtr mViewModel; diff --git a/indra/llui/lluictrlfactory.cpp b/indra/llui/lluictrlfactory.cpp index 586b988c43..1161101f90 100644 --- a/indra/llui/lluictrlfactory.cpp +++ b/indra/llui/lluictrlfactory.cpp @@ -45,51 +45,13 @@ #include "llquaternion.h" // this library includes -#include "llbutton.h" -#include "llcheckboxctrl.h" -//#include "llcolorswatch.h" -#include "llcombobox.h" -#include "llcontrol.h" -#include "lldir.h" -#include "llevent.h" #include "llfloater.h" -#include "lliconctrl.h" -#include "lllineeditor.h" -#include "llmenugl.h" -#include "llradiogroup.h" -#include "llscrollcontainer.h" -#include "llscrollingpanellist.h" -#include "llscrolllistctrl.h" -#include "llslider.h" -#include "llsliderctrl.h" -#include "llmultislider.h" -#include "llmultisliderctrl.h" -#include "llspinctrl.h" -#include "lltabcontainer.h" -#include "lltextbox.h" -#include "lltexteditor.h" -#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"); //----------------------------------------------------------------------------- -// Register widgets that are purely data driven here so they get linked in -#include "llstatview.h" -static LLDefaultChildRegistry::Register<LLStatView> - register_stat_view("stat_view"); - -//----------------------------------------------------------------------------- // UI Ctrl class for padding class LLUICtrlLocate : public LLUICtrl @@ -163,7 +125,7 @@ void LLUICtrlFactory::createChildren(LLView* viewp, LLXMLNodePtr node, const wid } -LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing"); +static LLFastTimer::DeclareTimer FTM_XML_PARSE("XML Reading/Parsing"); //----------------------------------------------------------------------------- // getLayeredXMLNode() //----------------------------------------------------------------------------- @@ -191,14 +153,14 @@ bool LLUICtrlFactory::getLocalizedXMLNode(const std::string &xui_filename, LLXML } } -static LLFastTimer::DeclareTimer BUILD_FLOATERS("Build Floaters"); +static LLFastTimer::DeclareTimer FTM_BUILD_FLOATERS("Build Floaters"); //----------------------------------------------------------------------------- // buildFloater() //----------------------------------------------------------------------------- void LLUICtrlFactory::buildFloater(LLFloater* floaterp, const std::string& filename, LLXMLNodePtr output_node) { - LLFastTimer timer(BUILD_FLOATERS); + LLFastTimer timer(FTM_BUILD_FLOATERS); LLXMLNodePtr root; //if exporting, only load the language being exported, @@ -262,14 +224,14 @@ S32 LLUICtrlFactory::saveToXML(LLView* viewp, const std::string& filename) return 0; } -static LLFastTimer::DeclareTimer BUILD_PANELS("Build Panels"); +static LLFastTimer::DeclareTimer FTM_BUILD_PANELS("Build Panels"); //----------------------------------------------------------------------------- // buildPanel() //----------------------------------------------------------------------------- BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, LLXMLNodePtr output_node) { - LLFastTimer timer(BUILD_PANELS); + LLFastTimer timer(FTM_BUILD_PANELS); BOOL didPost = FALSE; LLXMLNodePtr root; @@ -331,7 +293,7 @@ BOOL LLUICtrlFactory::buildPanel(LLPanel* panelp, const std::string& filename, L //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget"); +static LLFastTimer::DeclareTimer FTM_CREATE_FROM_XML("Create child widget"); LLView *LLUICtrlFactory::createFromXML(LLXMLNodePtr node, LLView* parent, const std::string& filename, const widget_registry_t& registry, LLXMLNodePtr output_node) { @@ -442,929 +404,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/llui/lluistring.cpp b/indra/llui/lluistring.cpp index 7ce0fd7a88..20ff71378e 100644 --- a/indra/llui/lluistring.cpp +++ b/indra/llui/lluistring.cpp @@ -37,6 +37,8 @@ const LLStringUtil::format_map_t LLUIString::sNullArgs; +LLFastTimer::DeclareTimer FTM_UI_STRING("UI String"); + LLUIString::LLUIString(const std::string& instring, const LLStringUtil::format_map_t& args) : mOrig(instring), @@ -59,6 +61,8 @@ void LLUIString::setArgList(const LLStringUtil::format_map_t& args) void LLUIString::setArgs(const LLSD& sd) { + LLFastTimer timer(FTM_UI_STRING); + if (!sd.isMap()) return; for(LLSD::map_const_iterator sd_it = sd.beginMap(); sd_it != sd.endMap(); @@ -112,6 +116,8 @@ void LLUIString::clear() void LLUIString::format() { + LLFastTimer timer(FTM_UI_STRING); + // optimize for empty strings (don't attempt string replacement) if (mOrig.empty()) { diff --git a/indra/llui/llview.cpp b/indra/llui/llview.cpp index e3b61dfaa2..4770807ac7 100644 --- a/indra/llui/llview.cpp +++ b/indra/llui/llview.cpp @@ -145,12 +145,6 @@ LLView::~LLView() // llassert_always(sDepth == 0); // avoid deleting views while drawing! It can subtly break list iterators - if( gFocusMgr.getKeyboardFocus() == this ) - { - //llwarns << "View holding keyboard focus deleted: " << getName() << ". Keyboard focus removed." << llendl; - gFocusMgr.removeKeyboardFocusWithoutCallback( this ); - } - if( hasMouseCapture() ) { //llwarns << "View holding mouse capture deleted: " << getName() << ". Mouse capture removed." << llendl; @@ -947,16 +941,11 @@ BOOL LLView::handleDoubleClick(S32 x, S32 y, MASK mask) BOOL LLView::handleScrollWheel(S32 x, S32 y, S32 clicks) { - BOOL handled = FALSE; if( getVisible() && getEnabled() ) { - handled = childrenHandleScrollWheel( x, y, clicks ) != NULL; - if( !handled && blockMouseEvent(x, y) ) - { - handled = TRUE; - } + return childrenHandleScrollWheel( x, y, clicks ) != NULL; } - return handled; + return FALSE; } BOOL LLView::handleRightMouseDown(S32 x, S32 y, MASK mask) @@ -1306,6 +1295,11 @@ LLView* LLView::childrenHandleMiddleMouseUp(S32 x, S32 y, MASK mask) void LLView::draw() { + drawChildren(); +} + +void LLView::drawChildren() +{ if (sDebugRects) { drawDebugRect(); @@ -1334,7 +1328,7 @@ void LLView::draw() { // Only draw views that are within the root view localRectToScreen(viewp->getRect(),&screenRect); - if ( rootRect.rectInRect(&screenRect) ) + if ( rootRect.overlaps(screenRect) ) { glMatrixMode(GL_MODELVIEW); LLUI::pushMatrix(); @@ -1545,7 +1539,7 @@ void LLView::updateBoundingRect() LLRect child_bounding_rect = childp->getBoundingRect(); - if (local_bounding_rect.isNull()) + if (local_bounding_rect.isEmpty()) { // start out with bounding rect equal to first visible child's bounding rect local_bounding_rect = child_bounding_rect; @@ -1553,7 +1547,7 @@ void LLView::updateBoundingRect() else { // accumulate non-null children rectangles - if (!child_bounding_rect.isNull()) + if (!child_bounding_rect.isEmpty()) { local_bounding_rect.unionWith(child_bounding_rect); } @@ -1644,7 +1638,7 @@ BOOL LLView::hasAncestor(const LLView* parentp) const BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const { - LLView *child = getChildView(childname, TRUE, FALSE); + LLView *child = findChildView(childname, TRUE); if (child) { return gFocusMgr.childHasKeyboardFocus(child); @@ -1659,13 +1653,27 @@ BOOL LLView::childHasKeyboardFocus( const std::string& childname ) const BOOL LLView::hasChild(const std::string& childname, BOOL recurse) const { - return getChildView(childname, recurse, FALSE) != NULL; + return findChildView(childname, recurse) != NULL; } //----------------------------------------------------------------------------- // getChildView() //----------------------------------------------------------------------------- -LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_if_missing) const +LLView* LLView::getChildView(const std::string& name, BOOL recurse) const +{ + LLView* child = findChildView(name, recurse); + if (!child) + { + child = getDefaultWidget<LLView>(name); + if (!child) + { + child = LLUICtrlFactory::createDefaultWidget<LLView>(name); + } + } + return child; +} + +LLView* LLView::findChildView(const std::string& name, BOOL recurse) const { //richard: should we allow empty names? //if(name.empty()) @@ -1686,23 +1694,13 @@ LLView* LLView::getChildView(const std::string& name, BOOL recurse, BOOL create_ for ( child_it = mChildList.begin(); child_it != mChildList.end(); ++child_it) { LLView* childp = *child_it; - LLView* viewp = childp->getChildView(name, recurse, FALSE); + LLView* viewp = childp->findChildView(name, recurse); if ( viewp ) { return viewp; } } } - - if (create_if_missing) - { - LLView* view = getDefaultWidget<LLView>(name); - if (!view) - { - view = LLUICtrlFactory::createDefaultWidget<LLView>(name); - } - return view; - } return NULL; } diff --git a/indra/llui/llview.h b/indra/llui/llview.h index ee49276139..ecc6bf47da 100644 --- a/indra/llui/llview.h +++ b/indra/llui/llview.h @@ -54,6 +54,7 @@ #include "llcursortypes.h" #include "lluictrlfactory.h" #include "lltreeiterators.h" +#include "llfocusmgr.h" #include <list> @@ -141,7 +142,7 @@ virtual BOOL handleUnicodeCharHere(llwchar uni_char); class LLViewWidgetRegistry : public LLChildRegistry<LLViewWidgetRegistry> {}; -class LLView : public LLMouseHandler, public LLMortician +class LLView : public LLMouseHandler, public LLMortician, public LLFocusableElement { public: struct Follows : public LLInitParam::Choice<Follows> @@ -213,6 +214,9 @@ protected: LLView(const LLView::Params&); friend class LLUICtrlFactory; +private: + // widgets in general are not copyable + LLView(const LLView& other) {}; public: #if LL_DEBUG static BOOL sIsDrawing; @@ -410,8 +414,10 @@ public: virtual BOOL canSnapTo(const LLView* other_view); virtual void setSnappedTo(const LLView* snap_view); - virtual BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); - virtual BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + // inherited from LLFocusableElement + /* virtual */ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); + /* virtual */ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + virtual BOOL handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, EDragAndDropType cargo_type, void* cargo_data, @@ -421,6 +427,7 @@ public: virtual std::string getShowNamesToolTip(); virtual void draw(); + void drawChildren(); void parseFollowsFlags(const LLView::Params& params); @@ -428,8 +435,9 @@ public: BOOL getSaveToXML() const { return mSaveToXML; } void setSaveToXML(BOOL b) { mSaveToXML = b; } - virtual void onFocusLost(); - virtual void onFocusReceived(); + // inherited from LLFocusableElement + /* virtual */ void onFocusLost(); + /* virtual */ void onFocusReceived(); typedef enum e_hit_test_type { @@ -484,19 +492,20 @@ public: template <class T> T* findChild(const std::string& name, BOOL recurse = TRUE) const { - LLView* child = getChildView(name, recurse, FALSE); + LLView* child = findChildView(name, recurse); T* result = dynamic_cast<T*>(child); return result; } - template <class T> T* getChild(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const; + template <class T> T* getChild(const std::string& name, BOOL recurse = TRUE) const; template <class T> T& getChildRef(const std::string& name, BOOL recurse = TRUE) const { - return *getChild<T>(name, recurse, TRUE); + return *getChild<T>(name, recurse); } - virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE, BOOL create_if_missing = TRUE) const; + virtual LLView* getChildView(const std::string& name, BOOL recurse = TRUE) const; + virtual LLView* findChildView(const std::string& name, BOOL recurse = TRUE) const; template <class T> T* getDefaultWidget(const std::string& name) const { @@ -636,9 +645,9 @@ private: LLView::child_tab_order_t mTabOrder; }; -template <class T> T* LLView::getChild(const std::string& name, BOOL recurse, BOOL create_if_missing) const +template <class T> T* LLView::getChild(const std::string& name, BOOL recurse) const { - LLView* child = getChildView(name, recurse, FALSE); + LLView* child = findChildView(name, recurse); T* result = dynamic_cast<T*>(child); if (!result) { @@ -647,28 +656,25 @@ template <class T> T* LLView::getChild(const std::string& name, BOOL recurse, BO { llwarns << "Found child named " << name << " but of wrong type " << typeid(child).name() << ", expecting " << typeid(T*).name() << llendl; } - if (create_if_missing) + result = getDefaultWidget<T>(name); + if (!result) { - result = getDefaultWidget<T>(name); - if (!result) + result = LLUICtrlFactory::getDefaultWidget<T>(name); + + if (result) { - result = LLUICtrlFactory::getDefaultWidget<T>(name); - - if (result) - { - // *NOTE: You cannot call mFoo = getChild<LLFoo>("bar") - // in a floater or panel constructor. The widgets will not - // be ready. Instead, put it in postBuild(). - llwarns << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << llendl; - } - else - { - llwarns << "Failed to create dummy " << typeid(T).name() << llendl; - return NULL; - } - - getDefaultWidgetMap()[name] = result; + // *NOTE: You cannot call mFoo = getChild<LLFoo>("bar") + // in a floater or panel constructor. The widgets will not + // be ready. Instead, put it in postBuild(). + llwarns << "Making dummy " << typeid(T).name() << " named \"" << name << "\" in " << getName() << llendl; } + else + { + llwarns << "Failed to create dummy " << typeid(T).name() << llendl; + return NULL; + } + + getDefaultWidgetMap()[name] = result; } } return result; diff --git a/indra/llui/llviewborder.cpp b/indra/llui/llviewborder.cpp index 860aa3302e..f41c98f7b3 100644 --- a/indra/llui/llviewborder.cpp +++ b/indra/llui/llviewborder.cpp @@ -134,9 +134,7 @@ void LLViewBorder::draw() } } - // draw the children - LLView::draw(); - + drawChildren(); } void LLViewBorder::drawOnePixelLines() diff --git a/indra/llvfs/lldir.cpp b/indra/llvfs/lldir.cpp index 0c65047d4d..52ef1baabf 100644 --- a/indra/llvfs/lldir.cpp +++ b/indra/llvfs/lldir.cpp @@ -296,6 +296,10 @@ const std::string LLDir::getSkinBaseDir() const return mSkinBaseDir; } +const std::string &LLDir::getLLPluginDir() const +{ + return mLLPluginDir; +} std::string LLDir::getExpandedFilename(ELLPath location, const std::string& filename) const { diff --git a/indra/llvfs/lldir.h b/indra/llvfs/lldir.h index a1f9df007d..6c9fea6b6a 100644 --- a/indra/llvfs/lldir.h +++ b/indra/llvfs/lldir.h @@ -88,6 +88,10 @@ class LLDir const std::string findFile(const std::string& filename, const std::vector<std::string> filenames) const; const std::string findFile(const std::string& filename, const std::string& searchPath1 = "", const std::string& searchPath2 = "", const std::string& searchPath3 = "") const; + + virtual std::string getLLPluginLauncher() = 0; // full path and name for the plugin shell + virtual std::string getLLPluginFilename(std::string base_name) = 0; // full path and name to the plugin DSO for this base_name (i.e. 'FOO' -> '/bar/baz/libFOO.so') + const std::string &getExecutablePathAndName() const; // Full pathname of the executable const std::string &getAppName() const; // install directory under progams/ ie "SecondLife" const std::string &getExecutableDir() const; // Directory where the executable is located @@ -108,6 +112,7 @@ class LLDir const std::string &getUserSkinDir() const; // User-specified skin folder with user modifications. e.g. c:\documents and settings\username\application data\second life\skins\curskin const std::string &getDefaultSkinDir() const; // folder for default skin. e.g. c:\program files\second life\skins\default const std::string getSkinBaseDir() const; // folder that contains all installed skins (not user modifications). e.g. c:\program files\second life\skins + const std::string &getLLPluginDir() const; // Directory containing plugins and plugin shell // Expanded filename std::string getExpandedFilename(ELLPath location, const std::string &filename) const; @@ -165,6 +170,7 @@ protected: std::string mSkinDir; // Location for current skin info. std::string mDefaultSkinDir; // Location for default skin info. std::string mUserSkinDir; // Location for user-modified skin info. + std::string mLLPluginDir; // Location for plugins and plugin shell }; void dir_exists_or_crash(const std::string &dir_name); diff --git a/indra/llvfs/lldir_linux.cpp b/indra/llvfs/lldir_linux.cpp index d7a0dc8528..24efcb8ae8 100644 --- a/indra/llvfs/lldir_linux.cpp +++ b/indra/llvfs/lldir_linux.cpp @@ -129,6 +129,8 @@ LLDir_Linux::LLDir_Linux() } } + mLLPluginDir = mExecutableDir + mDirDelimiter + "llplugin"; + // *TODO: don't use /tmp, use $HOME/.secondlife/tmp or something. mTempDir = "/tmp"; } @@ -378,3 +380,15 @@ BOOL LLDir_Linux::fileExists(const std::string &filename) const } } + +/*virtual*/ std::string LLDir_Linux::getLLPluginLauncher() +{ + return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() + + "SLPlugin"; +} + +/*virtual*/ std::string LLDir_Linux::getLLPluginFilename(std::string base_name) +{ + return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() + + "lib" + base_name + ".so"; +} diff --git a/indra/llvfs/lldir_linux.h b/indra/llvfs/lldir_linux.h index a78a9854ff..1fec15c5c6 100644 --- a/indra/llvfs/lldir_linux.h +++ b/indra/llvfs/lldir_linux.h @@ -53,6 +53,9 @@ public: virtual void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ std::string getLLPluginLauncher(); + /*virtual*/ std::string getLLPluginFilename(std::string base_name); + private: DIR *mDirp; int mCurrentDirIndex; diff --git a/indra/llvfs/lldir_mac.cpp b/indra/llvfs/lldir_mac.cpp index 7f703464bc..9be787df11 100644 --- a/indra/llvfs/lldir_mac.cpp +++ b/indra/llvfs/lldir_mac.cpp @@ -143,14 +143,18 @@ LLDir_Mac::LLDir_Mac() // mAppRODataDir - CFURLRef resourcesURLRef = CFBundleCopyResourcesDirectoryURL(mainBundleRef); - CFURLRefToLLString(resourcesURLRef, mAppRODataDir, true); // *NOTE: When running in a dev tree, use the copy of // skins in indra/newview/ rather than in the application bundle. This // mirrors Windows dev environment behavior and allows direct checkin // of edited skins/xui files. JC + + // MBW -- This keeps the mac application from finding other things. + // If this is really for skins, it should JUST apply to skins. + CFURLRef resourcesURLRef = CFBundleCopyResourcesDirectoryURL(mainBundleRef); + CFURLRefToLLString(resourcesURLRef, mAppRODataDir, true); + U32 indra_pos = mExecutableDir.find("/indra"); if (indra_pos != std::string::npos) { @@ -211,6 +215,8 @@ LLDir_Mac::LLDir_Mac() } mWorkingDir = getCurPath(); + + mLLPluginDir = mAppRODataDir + mDirDelimiter + "llplugin"; CFRelease(executableURLRef); executableURLRef = NULL; @@ -416,4 +422,17 @@ BOOL LLDir_Mac::fileExists(const std::string &filename) const } +/*virtual*/ std::string LLDir_Mac::getLLPluginLauncher() +{ + return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() + + "SLPlugin"; +} + +/*virtual*/ std::string LLDir_Mac::getLLPluginFilename(std::string base_name) +{ + return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() + + base_name + ".dylib"; +} + + #endif // LL_DARWIN diff --git a/indra/llvfs/lldir_mac.h b/indra/llvfs/lldir_mac.h index 82ac94ed4c..834acc7262 100644 --- a/indra/llvfs/lldir_mac.h +++ b/indra/llvfs/lldir_mac.h @@ -53,6 +53,9 @@ public: virtual void getRandomFileInDir(const std::string &dirname, const std::string &ask, std::string &fname); virtual BOOL fileExists(const std::string &filename) const; + /*virtual*/ std::string getLLPluginLauncher(); + /*virtual*/ std::string getLLPluginFilename(std::string base_name); + private: int mCurrentDirIndex; int mCurrentDirCount; diff --git a/indra/llvfs/lldir_solaris.cpp b/indra/llvfs/lldir_solaris.cpp index ba71bc0eb4..a21f3ca0ab 100644 --- a/indra/llvfs/lldir_solaris.cpp +++ b/indra/llvfs/lldir_solaris.cpp @@ -161,6 +161,8 @@ LLDir_Solaris::LLDir_Solaris() } } + mLLPluginDir = mExecutableDir + mDirDelimiter + "llplugin"; + // *TODO: don't use /tmp, use $HOME/.secondlife/tmp or something. mTempDir = "/tmp"; } diff --git a/indra/llvfs/lldir_win32.cpp b/indra/llvfs/lldir_win32.cpp index da80a95922..872f2cf1c1 100644 --- a/indra/llvfs/lldir_win32.cpp +++ b/indra/llvfs/lldir_win32.cpp @@ -144,6 +144,8 @@ LLDir_Win32::LLDir_Win32() llwarns << "Couldn't create LL_PATH_CACHE dir " << mDefaultCacheDir << llendl; } } + + mLLPluginDir = mExecutableDir + mDirDelimiter + "llplugin"; } LLDir_Win32::~LLDir_Win32() @@ -393,6 +395,19 @@ BOOL LLDir_Win32::fileExists(const std::string &filename) const } +/*virtual*/ std::string LLDir_Win32::getLLPluginLauncher() +{ + return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() + + "SLPlugin.exe"; +} + +/*virtual*/ std::string LLDir_Win32::getLLPluginFilename(std::string base_name) +{ + return gDirUtilp->getLLPluginDir() + gDirUtilp->getDirDelimiter() + + base_name + ".dll"; +} + + #if 0 // Utility function to get version number of a DLL diff --git a/indra/llvfs/lldir_win32.h b/indra/llvfs/lldir_win32.h index d2497901e4..c2acfa6bd4 100644 --- a/indra/llvfs/lldir_win32.h +++ b/indra/llvfs/lldir_win32.h @@ -50,6 +50,9 @@ public: /*virtual*/ void getRandomFileInDir(const std::string &dirname, const std::string &mask, std::string &fname); /*virtual*/ BOOL fileExists(const std::string &filename) const; + /*virtual*/ std::string getLLPluginLauncher(); + /*virtual*/ std::string getLLPluginFilename(std::string base_name); + private: BOOL LLDir_Win32::getNextFileInDir(const llutf16string &dirname, const std::string &mask, std::string &fname, BOOL wrap); diff --git a/indra/llvfs/llvfile.cpp b/indra/llvfs/llvfile.cpp index 6b1563bab1..5fdf41188d 100644 --- a/indra/llvfs/llvfile.cpp +++ b/indra/llvfs/llvfile.cpp @@ -44,6 +44,8 @@ const S32 LLVFile::WRITE = 0x00000002; const S32 LLVFile::READ_WRITE = 0x00000003; // LLVFile::READ & LLVFile::WRITE const S32 LLVFile::APPEND = 0x00000006; // 0x00000004 & LLVFile::WRITE +static LLFastTimer::DeclareTimer FTM_VFILE_WAIT("VFile Wait"); + //---------------------------------------------------------------------------- LLVFSThread* LLVFile::sVFSThread = NULL; BOOL LLVFile::sAllocdVFSThread = FALSE; @@ -318,7 +320,7 @@ BOOL LLVFile::setMaxSize(S32 size) if (!mVFS->checkAvailable(size)) { - LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT); + LLFastTimer t(FTM_VFILE_WAIT); S32 count = 0; while (sVFSThread->getPending() > 1000) { @@ -426,7 +428,7 @@ bool LLVFile::isLocked(EVFSLock lock) void LLVFile::waitForLock(EVFSLock lock) { - LLFastTimer t(LLFastTimer::FTM_VFILE_WAIT); + LLFastTimer t(FTM_VFILE_WAIT); // spin until the lock clears while (isLocked(lock)) { diff --git a/indra/llwindow/CMakeLists.txt b/indra/llwindow/CMakeLists.txt index beaf5c3488..7b1cab696f 100644 --- a/indra/llwindow/CMakeLists.txt +++ b/indra/llwindow/CMakeLists.txt @@ -19,7 +19,6 @@ include(LLRender) include(LLVFS) include(LLWindow) include(LLXML) -include(Mozlib) include(UI) include_directories( 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/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index ce1bc82168..9936b24292 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -1645,6 +1645,9 @@ void LLWindowWin32::gatherInput() mMousePositionModified = FALSE; } +static LLFastTimer::DeclareTimer FTM_KEYHANDLER("Handle Keyboard"); +static LLFastTimer::DeclareTimer FTM_MOUSEHANDLER("Handle Mouse"); + LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_param, LPARAM l_param) { LLWindowWin32 *window_imp = (LLWindowWin32 *)GetWindowLong(h_wnd, GWL_USERDATA); @@ -1878,7 +1881,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_KEYUP: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_KEYUP"); - LLFastTimer t2(LLFastTimer::FTM_KEYHANDLER); + LLFastTimer t2(FTM_KEYHANDLER); if (gDebugWindowProc) { @@ -1987,7 +1990,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_LBUTTONDOWN: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONDOWN"); - LLFastTimer t2(LLFastTimer::FTM_MOUSEHANDLER); + LLFastTimer t2(FTM_MOUSEHANDLER); if (LLWinImm::isAvailable() && window_imp->mPreeditor) { window_imp->interruptLanguageTextInput(); @@ -2051,7 +2054,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_LBUTTONUP: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_LBUTTONUP"); - LLFastTimer t2(LLFastTimer::FTM_MOUSEHANDLER); + LLFastTimer t2(FTM_MOUSEHANDLER); //if (gDebugClicks) //{ // LL_INFOS("Window") << "WndProc left button up" << LL_ENDL; @@ -2085,7 +2088,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_RBUTTONDOWN: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_RBUTTONDOWN"); - LLFastTimer t2(LLFastTimer::FTM_MOUSEHANDLER); + LLFastTimer t2(FTM_MOUSEHANDLER); if (LLWinImm::isAvailable() && window_imp->mPreeditor) { window_imp->interruptLanguageTextInput(); @@ -2119,7 +2122,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_RBUTTONUP: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_RBUTTONUP"); - LLFastTimer t2(LLFastTimer::FTM_MOUSEHANDLER); + LLFastTimer t2(FTM_MOUSEHANDLER); // Because we move the cursor position in the app, we need to query // to find out where the cursor at the time the event is handled. // If we don't do this, many clicks could get buffered up, and if the @@ -2149,7 +2152,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ // case WM_MBUTTONDBLCLK: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MBUTTONDOWN"); - LLFastTimer t2(LLFastTimer::FTM_MOUSEHANDLER); + LLFastTimer t2(FTM_MOUSEHANDLER); if (LLWinImm::isAvailable() && window_imp->mPreeditor) { window_imp->interruptLanguageTextInput(); @@ -2183,7 +2186,7 @@ LRESULT CALLBACK LLWindowWin32::mainWindowProc(HWND h_wnd, UINT u_msg, WPARAM w_ case WM_MBUTTONUP: { window_imp->mCallbacks->handlePingWatchdog(window_imp, "Main:WM_MBUTTONUP"); - LLFastTimer t2(LLFastTimer::FTM_MOUSEHANDLER); + LLFastTimer t2(FTM_MOUSEHANDLER); // Because we move the cursor position in tllviewerhe app, we need to query // to find out where the cursor at the time the event is handled. // If we don't do this, many clicks could get buffered up, and if the diff --git a/indra/llxml/llcontrol.h b/indra/llxml/llcontrol.h index 0864940866..7d94601568 100644 --- a/indra/llxml/llcontrol.h +++ b/indra/llxml/llcontrol.h @@ -96,7 +96,7 @@ typedef enum e_control_type TYPE_COUNT } eControlType; -class LLControlVariable : public LLRefCount, boost::noncopyable +class LLControlVariable : public LLRefCount { friend class LLControlGroup; 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(¶m); + } + else + { + // don't use insert, since we want to overwrite existing entries + block_data.mNamedParams[name] = ¶m; + } + + 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(¶m); + + // 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..a1d0831939 --- /dev/null +++ b/indra/llxuixml/llinitparam.h @@ -0,0 +1,1823 @@ +/** + * @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*)¶m); + } + 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*)¶m, 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 isDefault() const { return mChangeVersion == 0; } + + 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..2efc475f57 --- /dev/null +++ b/indra/llxuixml/lltrans.cpp @@ -0,0 +1,196 @@ +/** + * @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+")"; + } +} + +//static +bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args) +{ + 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); + result = text; + return true; + } + 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); + + return false; + } +} diff --git a/indra/llxuixml/lltrans.h b/indra/llxuixml/lltrans.h new file mode 100644 index 0000000000..340d70e434 --- /dev/null +++ b/indra/llxuixml/lltrans.h @@ -0,0 +1,119 @@ +/** + * @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); + static bool findString(std::string &result, 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); + } + + static bool findString(std::string &result, const std::string &xml_desc) + { + LLStringUtil::format_map_t empty; + return findString(result, 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..e28e52fd16 --- /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 FTM_PARSE_XUI("XUI Parsing"); + +void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, bool silent) +{ + LLFastTimer timer(FTM_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/lscript/lscript_execute/lscript_execute.cpp b/indra/lscript/lscript_execute/lscript_execute.cpp index 05fee9a419..d7f445d3d8 100644 --- a/indra/lscript/lscript_execute/lscript_execute.cpp +++ b/indra/lscript/lscript_execute/lscript_execute.cpp @@ -4280,7 +4280,6 @@ BOOL run_calllib(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &id) } { - // LLFastTimer time_in_libraries1(LLFastTimer::FTM_TEMP7); gScriptLibrary.mFunctions[arg]->mExecFunc(returnvalue, arguments, id); } add_register_fp(buffer, LREG_ESR, -gScriptLibrary.mFunctions[arg]->mEnergyUse); @@ -4351,7 +4350,6 @@ BOOL run_calllib_two_byte(U8 *buffer, S32 &offset, BOOL b_print, const LLUUID &i } { - // LLFastTimer time_in_libraries2(LLFastTimer::FTM_TEMP8); gScriptLibrary.mFunctions[arg]->mExecFunc(returnvalue, arguments, id); } add_register_fp(buffer, LREG_ESR, -gScriptLibrary.mFunctions[arg]->mEnergyUse); diff --git a/indra/media_plugins/CMakeLists.txt b/indra/media_plugins/CMakeLists.txt new file mode 100644 index 0000000000..09f8acdb52 --- /dev/null +++ b/indra/media_plugins/CMakeLists.txt @@ -0,0 +1,16 @@ +# -*- cmake -*- + +add_subdirectory(base) + +add_subdirectory(webkit) + +add_subdirectory(gstreamer010) + +if (WINDOWS OR DARWIN) + add_subdirectory(quicktime) + add_subdirectory(awesomium) +endif (WINDOWS OR DARWIN) + +if (WINDOWS) + add_subdirectory(flash_activex) +endif(WINDOWS) diff --git a/indra/media_plugins/base/CMakeLists.txt b/indra/media_plugins/base/CMakeLists.txt new file mode 100644 index 0000000000..f8d2dabc6c --- /dev/null +++ b/indra/media_plugins/base/CMakeLists.txt @@ -0,0 +1,41 @@ +# -*- cmake -*- + +project(media_plugin_base) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(FindOpenGL) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} +) + + +### media_plugin_base + +set(media_plugin_base_SOURCE_FILES + media_plugin_base.cpp +) + +set(media_plugin_base_HEADER_FILES + CMakeLists.txt + + media_plugin_base.h +) + +add_library(media_plugin_base + ${media_plugin_base_SOURCE_FILES} +) + diff --git a/indra/media_plugins/base/media_plugin_base.cpp b/indra/media_plugins/base/media_plugin_base.cpp new file mode 100644 index 0000000000..0b7092fad6 --- /dev/null +++ b/indra/media_plugins/base/media_plugin_base.cpp @@ -0,0 +1,154 @@ +/** + * @file media_plugin_base.cpp + * @brief Media plugin base class for LLMedia API plugin system + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "media_plugin_base.h" + + +// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint +//////////////////////////////////////////////////////////////////////////////// +// + +MediaPluginBase::MediaPluginBase( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) +{ + mHostSendFunction = host_send_func; + mHostUserData = host_user_data; + mDeleteMe = false; + mPixels = 0; + mWidth = 0; + mHeight = 0; + mTextureWidth = 0; + mTextureHeight = 0; + mDepth = 0; + mStatus = STATUS_NONE; +} + +std::string MediaPluginBase::statusString() +{ + std::string result; + + switch(mStatus) + { + case STATUS_LOADING: result = "loading"; break; + case STATUS_LOADED: result = "loaded"; break; + case STATUS_ERROR: result = "error"; break; + case STATUS_PLAYING: result = "playing"; break; + case STATUS_PAUSED: result = "paused"; break; + default: + // keep the empty string + break; + } + + return result; +} + +void MediaPluginBase::setStatus(EStatus status) +{ + if(mStatus != status) + { + mStatus = status; + sendStatus(); + } +} + + +void MediaPluginBase::staticReceiveMessage(const char *message_string, void **user_data) +{ + MediaPluginBase *self = (MediaPluginBase*)*user_data; + + if(self != NULL) + { + self->receiveMessage(message_string); + + // If the plugin has processed the delete message, delete it. + if(self->mDeleteMe) + { + delete self; + *user_data = NULL; + } + } +} + +void MediaPluginBase::sendMessage(const LLPluginMessage &message) +{ + std::string output = message.generate(); + mHostSendFunction(output.c_str(), &mHostUserData); +} + +void MediaPluginBase::setDirty(int left, int top, int right, int bottom) +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); + + message.setValueS32("left", left); + message.setValueS32("top", top); + message.setValueS32("right", right); + message.setValueS32("bottom", bottom); + + sendMessage(message); +} + +void MediaPluginBase::sendStatus() +{ + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "media_status"); + + message.setValue("status", statusString()); + + sendMessage(message); +} + + +#if LL_WINDOWS +# define LLSYMEXPORT __declspec(dllexport) +#elif LL_LINUX +# define LLSYMEXPORT __attribute__ ((visibility("default"))) +#else +# define LLSYMEXPORT /**/ +#endif + +extern "C" +{ + LLSYMEXPORT int LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data); +} + +LLSYMEXPORT int +LLPluginInitEntryPoint(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + return init_media_plugin(host_send_func, host_user_data, plugin_send_func, plugin_user_data); +} + +#ifdef WIN32 +int WINAPI DllEntryPoint( HINSTANCE hInstance, unsigned long reason, void* params ) +{ + return 1; +} +#endif diff --git a/indra/media_plugins/base/media_plugin_base.exp b/indra/media_plugins/base/media_plugin_base.exp new file mode 100644 index 0000000000..d7a945a1c5 --- /dev/null +++ b/indra/media_plugins/base/media_plugin_base.exp @@ -0,0 +1 @@ +_LLPluginInitEntryPoint
\ No newline at end of file diff --git a/indra/media_plugins/base/media_plugin_base.h b/indra/media_plugins/base/media_plugin_base.h new file mode 100644 index 0000000000..8f600cb8d6 --- /dev/null +++ b/indra/media_plugins/base/media_plugin_base.h @@ -0,0 +1,111 @@ +/** + * @file media_plugin_base.h + * @brief Media plugin base class for LLMedia API plugin system + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" + + +class MediaPluginBase +{ +public: + MediaPluginBase(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + virtual ~MediaPluginBase() {} + + virtual void receiveMessage(const char *message_string) = 0; + + static void staticReceiveMessage(const char *message_string, void **user_data); + +protected: + + typedef enum + { + STATUS_NONE, + STATUS_LOADING, + STATUS_LOADED, + STATUS_ERROR, + STATUS_PLAYING, + STATUS_PAUSED, + } EStatus; + + class SharedSegmentInfo + { + public: + void *mAddress; + size_t mSize; + }; + + void sendMessage(const LLPluginMessage &message); + void sendStatus(); + std::string statusString(); + void setStatus(EStatus status); + + // The quicktime plugin overrides this to add current time and duration to the message... + virtual void setDirty(int left, int top, int right, int bottom); + + typedef std::map<std::string, SharedSegmentInfo> SharedSegmentMap; + + + LLPluginInstance::sendMessageFunction mHostSendFunction; + void *mHostUserData; + bool mDeleteMe; + unsigned char* mPixels; + std::string mTextureSegmentName; + int mWidth; + int mHeight; + int mTextureWidth; + int mTextureHeight; + int mDepth; + EStatus mStatus; + SharedSegmentMap mSharedSegments; + +}; + +// The plugin must define this function to create its instance. +int init_media_plugin( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data, + LLPluginInstance::sendMessageFunction *plugin_send_func, + void **plugin_user_data); + +// It should look something like this: +/* +{ + MediaPluginFoo *self = new MediaPluginFoo(host_send_func, host_user_data); + *plugin_send_func = MediaPluginFoo::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} +*/ + diff --git a/indra/media_plugins/gstreamer010/CMakeLists.txt b/indra/media_plugins/gstreamer010/CMakeLists.txt new file mode 100644 index 0000000000..5c0ce3ee17 --- /dev/null +++ b/indra/media_plugins/gstreamer010/CMakeLists.txt @@ -0,0 +1,71 @@ +# -*- cmake -*- + +project(media_plugin_gstreamer010) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(MediaPluginBase) +include(FindOpenGL) + +include(GStreamer010Plugin) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} + ${GSTREAMER010_INCLUDE_DIRS} + ${GSTREAMER010_PLUGINS_BASE_INCLUDE_DIRS} +) + +### media_plugin_gstreamer010 + +set(media_plugin_gstreamer010_SOURCE_FILES + media_plugin_gstreamer010.cpp + llmediaimplgstreamer_syms.cpp + llmediaimplgstreamervidplug.cpp + ) + +set(media_plugin_gstreamer010_HEADER_FILES + llmediaimplgstreamervidplug.h + llmediaimplgstreamer_syms.h + llmediaimplgstreamertriviallogging.h + ) + +if (${CXX_VERSION} MATCHES "4.[23]") + # Work around a bad interaction between broken gstreamer headers and + # g++ 4.3's increased strictness. + set_source_files_properties(llmediaimplgstreamervidplug.cpp PROPERTIES + COMPILE_FLAGS -Wno-error=write-strings) +endif (${CXX_VERSION} MATCHES "4.[23]") + +add_library(media_plugin_gstreamer010 + SHARED + ${media_plugin_gstreamer010_SOURCE_FILES} +) + +target_link_libraries(media_plugin_gstreamer010 + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} + ${GSTREAMER010_LIBRARIES} +) + +add_dependencies(media_plugin_gstreamer010 + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + + diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h new file mode 100644 index 0000000000..ef41736c18 --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer.h @@ -0,0 +1,57 @@ +/** + * @file llmediaimplgstreamer.h + * @author Tofu Linden + * @brief implementation that supports media playback via GStreamer. + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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$ + */ + +// header guard +#ifndef llmediaimplgstreamer_h +#define llmediaimplgstreamer_h + +#if LL_GSTREAMER010_ENABLED + +extern "C" { +#include <stdio.h> +#include <gst/gst.h> + +#include "apr_pools.h" +#include "apr_dso.h" +} + + +extern "C" { +gboolean llmediaimplgstreamer_bus_callback (GstBus *bus, + GstMessage *message, + gpointer data); +} + +#endif // LL_GSTREAMER010_ENABLED + +#endif // llmediaimplgstreamer_h diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp new file mode 100644 index 0000000000..cc52232496 --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.cpp @@ -0,0 +1,171 @@ +/** + * @file llmediaimplgstreamer_syms.cpp + * @brief dynamic GStreamer symbol-grabbing code + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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$ + */ + +#if LL_GSTREAMER010_ENABLED + +#include <string> + +extern "C" { +#include <gst/gst.h> + +#include "apr_pools.h" +#include "apr_dso.h" +} + +#include "llmediaimplgstreamertriviallogging.h" + +#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) RTN (*ll##GSTSYM)(__VA_ARGS__) = NULL +#include "llmediaimplgstreamer_syms_raw.inc" +#include "llmediaimplgstreamer_syms_rawv.inc" +#undef LL_GST_SYM + +// a couple of stubs for disgusting reasons +GstDebugCategory* +ll_gst_debug_category_new(gchar *name, guint color, gchar *description) +{ + static GstDebugCategory dummy; + return &dummy; +} +void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname) +{ +} + +static bool sSymsGrabbed = false; +static apr_pool_t *sSymGSTDSOMemoryPool = NULL; +static apr_dso_handle_t *sSymGSTDSOHandleG = NULL; +static apr_dso_handle_t *sSymGSTDSOHandleV = NULL; + + +bool grab_gst_syms(std::string gst_dso_name, + std::string gst_dso_name_vid) +{ + if (sSymsGrabbed) + { + // already have grabbed good syms + return TRUE; + } + + bool sym_error = false; + bool rtn = false; + apr_status_t rv; + apr_dso_handle_t *sSymGSTDSOHandle = NULL; + +#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{rv = apr_dso_sym((apr_dso_handle_sym_t*)&ll##GSTSYM, sSymGSTDSOHandle, #GSTSYM); if (rv != APR_SUCCESS) {INFOMSG("Failed to grab symbol: %s", #GSTSYM); if (REQ) sym_error = true;} else DEBUGMSG("grabbed symbol: %s from %p", #GSTSYM, (void*)ll##GSTSYM);}while(0) + + //attempt to load the shared libraries + apr_pool_create(&sSymGSTDSOMemoryPool, NULL); + + if ( APR_SUCCESS == (rv = apr_dso_load(&sSymGSTDSOHandle, + gst_dso_name.c_str(), + sSymGSTDSOMemoryPool) )) + { + INFOMSG("Found DSO: %s", gst_dso_name.c_str()); +#include "llmediaimplgstreamer_syms_raw.inc" + + if ( sSymGSTDSOHandle ) + { + sSymGSTDSOHandleG = sSymGSTDSOHandle; + sSymGSTDSOHandle = NULL; + } + + if ( APR_SUCCESS == + (rv = apr_dso_load(&sSymGSTDSOHandle, + gst_dso_name_vid.c_str(), + sSymGSTDSOMemoryPool) )) + { + INFOMSG("Found DSO: %s", gst_dso_name_vid.c_str()); +#include "llmediaimplgstreamer_syms_rawv.inc" + rtn = !sym_error; + } + else + { + INFOMSG("Couldn't load DSO: %s", gst_dso_name_vid.c_str()); + rtn = false; // failure + } + } + else + { + INFOMSG("Couldn't load DSO: %s", gst_dso_name.c_str()); + rtn = false; // failure + } + + if (sym_error) + { + WARNMSG("Failed to find necessary symbols in GStreamer libraries."); + } + + if ( sSymGSTDSOHandle ) + { + sSymGSTDSOHandleV = sSymGSTDSOHandle; + sSymGSTDSOHandle = NULL; + } +#undef LL_GST_SYM + + sSymsGrabbed = !!rtn; + return rtn; +} + + +void ungrab_gst_syms() +{ + // should be safe to call regardless of whether we've + // actually grabbed syms. + + if ( sSymGSTDSOHandleG ) + { + apr_dso_unload(sSymGSTDSOHandleG); + sSymGSTDSOHandleG = NULL; + } + + if ( sSymGSTDSOHandleV ) + { + apr_dso_unload(sSymGSTDSOHandleV); + sSymGSTDSOHandleV = NULL; + } + + if ( sSymGSTDSOMemoryPool ) + { + apr_pool_destroy(sSymGSTDSOMemoryPool); + sSymGSTDSOMemoryPool = NULL; + } + + // NULL-out all of the symbols we'd grabbed +#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) do{ll##GSTSYM = NULL;}while(0) +#include "llmediaimplgstreamer_syms_raw.inc" +#include "llmediaimplgstreamer_syms_rawv.inc" +#undef LL_GST_SYM + + sSymsGrabbed = false; +} + + +#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h new file mode 100644 index 0000000000..ee7473d6d1 --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms.h @@ -0,0 +1,78 @@ +/** + * @file llmediaimplgstreamer_syms.h + * @brief dynamic GStreamer symbol-grabbing code + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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" + +#if LL_GSTREAMER010_ENABLED + +extern "C" { +#include <gst/gst.h> +} + +bool grab_gst_syms(std::string gst_dso_name, + std::string gst_dso_name_vid); +void ungrab_gst_syms(); + +#define LL_GST_SYM(REQ, GSTSYM, RTN, ...) extern RTN (*ll##GSTSYM)(__VA_ARGS__) +#include "llmediaimplgstreamer_syms_raw.inc" +#include "llmediaimplgstreamer_syms_rawv.inc" +#undef LL_GST_SYM + +// regrettable hacks to give us better runtime compatibility with older systems +#define llg_return_if_fail(COND) do{if (!(COND)) return;}while(0) +#define llg_return_val_if_fail(COND,V) do{if (!(COND)) return V;}while(0) + +// regrettable hacks because GStreamer was not designed for runtime loading +#undef GST_TYPE_MESSAGE +#define GST_TYPE_MESSAGE (llgst_message_get_type()) +#undef GST_TYPE_OBJECT +#define GST_TYPE_OBJECT (llgst_object_get_type()) +#undef GST_TYPE_PIPELINE +#define GST_TYPE_PIPELINE (llgst_pipeline_get_type()) +#undef GST_TYPE_ELEMENT +#define GST_TYPE_ELEMENT (llgst_element_get_type()) +#undef GST_TYPE_VIDEO_SINK +#define GST_TYPE_VIDEO_SINK (llgst_video_sink_get_type()) +// more regrettable hacks to stub-out these .h-exposed GStreamer internals +void ll_gst_debug_register_funcptr(GstDebugFuncPtr func, gchar* ptrname); +#undef _gst_debug_register_funcptr +#define _gst_debug_register_funcptr ll_gst_debug_register_funcptr +GstDebugCategory* ll_gst_debug_category_new(gchar *name, guint color, gchar *description); +#undef _gst_debug_category_new +#define _gst_debug_category_new ll_gst_debug_category_new +#undef __gst_debug_enabled +#define __gst_debug_enabled (0) + +// more hacks +#define LLGST_MESSAGE_TYPE_NAME(M) (llgst_message_type_get_name(GST_MESSAGE_TYPE(M))) + +#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc new file mode 100644 index 0000000000..b33e59363d --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_raw.inc @@ -0,0 +1,51 @@ + +// required symbols to grab +LL_GST_SYM(true, gst_pad_peer_accept_caps, gboolean, GstPad *pad, GstCaps *caps); +LL_GST_SYM(true, gst_buffer_new, GstBuffer*, void); +LL_GST_SYM(true, gst_buffer_set_caps, void, GstBuffer*, GstCaps *); +LL_GST_SYM(true, gst_structure_set_value, void, GstStructure *, const gchar *, const GValue*); +LL_GST_SYM(true, gst_init_check, gboolean, int *argc, char **argv[], GError ** err); +LL_GST_SYM(true, gst_message_get_type, GType, void); +LL_GST_SYM(true, gst_message_type_get_name, const gchar*, GstMessageType type); +LL_GST_SYM(true, gst_message_parse_error, void, GstMessage *message, GError **gerror, gchar **debug); +LL_GST_SYM(true, gst_message_parse_warning, void, GstMessage *message, GError **gerror, gchar **debug); +LL_GST_SYM(true, gst_message_parse_state_changed, void, GstMessage *message, GstState *oldstate, GstState *newstate, GstState *pending); +LL_GST_SYM(true, gst_element_set_state, GstStateChangeReturn, GstElement *element, GstState state); +LL_GST_SYM(true, gst_object_unref, void, gpointer object); +LL_GST_SYM(true, gst_object_get_type, GType, void); +LL_GST_SYM(true, gst_pipeline_get_type, GType, void); +LL_GST_SYM(true, gst_pipeline_get_bus, GstBus*, GstPipeline *pipeline); +LL_GST_SYM(true, gst_bus_add_watch, guint, GstBus * bus, GstBusFunc func, gpointer user_data); +LL_GST_SYM(true, gst_element_factory_make, GstElement*, const gchar *factoryname, const gchar *name); +LL_GST_SYM(true, gst_element_get_type, GType, void); +LL_GST_SYM(true, gst_static_pad_template_get, GstPadTemplate*, GstStaticPadTemplate *pad_template); +LL_GST_SYM(true, gst_element_class_add_pad_template, void, GstElementClass *klass, GstPadTemplate *temp); +LL_GST_SYM(true, gst_element_class_set_details, void, GstElementClass *klass, const GstElementDetails *details); +LL_GST_SYM(true, gst_caps_unref, void, GstCaps* caps); +LL_GST_SYM(true, gst_caps_ref, GstCaps *, GstCaps* caps); +//LL_GST_SYM(true, gst_caps_is_empty, gboolean, const GstCaps *caps); +LL_GST_SYM(true, gst_caps_from_string, GstCaps *, const gchar *string); +LL_GST_SYM(true, gst_caps_replace, void, GstCaps **caps, GstCaps *newcaps); +LL_GST_SYM(true, gst_caps_get_structure, GstStructure *, const GstCaps *caps, guint index); +LL_GST_SYM(true, gst_caps_copy, GstCaps *, const GstCaps * caps); +//LL_GST_SYM(true, gst_caps_intersect, GstCaps *, const GstCaps *caps1, const GstCaps *caps2); +LL_GST_SYM(true, gst_element_register, gboolean, GstPlugin *plugin, const gchar *name, guint rank, GType type); +LL_GST_SYM(true, _gst_plugin_register_static, void, GstPluginDesc *desc); +LL_GST_SYM(true, gst_structure_get_int, gboolean, const GstStructure *structure, const gchar *fieldname, gint *value); +LL_GST_SYM(true, gst_structure_get_value, G_CONST_RETURN GValue *, const GstStructure *structure, const gchar *fieldname); +LL_GST_SYM(true, gst_value_get_fraction_numerator, gint, const GValue *value); +LL_GST_SYM(true, gst_value_get_fraction_denominator, gint, const GValue *value); +LL_GST_SYM(true, gst_structure_get_name, G_CONST_RETURN gchar *, const GstStructure *structure); +LL_GST_SYM(true, gst_element_seek, bool, GstElement *, gdouble, GstFormat, GstSeekFlags, GstSeekType, gint64, GstSeekType, gint64); + +// optional symbols to grab +LL_GST_SYM(false, gst_registry_fork_set_enabled, void, gboolean enabled); +LL_GST_SYM(false, gst_segtrap_set_enabled, void, gboolean enabled); +LL_GST_SYM(false, gst_message_parse_buffering, void, GstMessage *message, gint *percent); +LL_GST_SYM(false, gst_message_parse_info, void, GstMessage *message, GError **gerror, gchar **debug); +LL_GST_SYM(false, gst_element_query_position, gboolean, GstElement *element, GstFormat *format, gint64 *cur); +LL_GST_SYM(false, gst_version, void, guint *major, guint *minor, guint *micro, guint *nano); + +// GStreamer 'internal' symbols which may not be visible in some runtimes but are still used in expanded GStreamer header macros - yuck! We'll substitute our own stubs for these. +//LL_GST_SYM(true, _gst_debug_register_funcptr, void, GstDebugFuncPtr func, gchar* ptrname); +//LL_GST_SYM(true, _gst_debug_category_new, GstDebugCategory *, gchar *name, guint color, gchar *description); diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc new file mode 100644 index 0000000000..14fbcb48b9 --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamer_syms_rawv.inc @@ -0,0 +1,5 @@ + +// required symbols to grab +LL_GST_SYM(true, gst_video_sink_get_type, GType, void); + +// optional symbols to grab diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h new file mode 100644 index 0000000000..e31d4a3282 --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamertriviallogging.h @@ -0,0 +1,53 @@ +/** + * @file llmediaimplgstreamertriviallogging.h + * @brief minimal logging utilities. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ +#define __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ + +#include <cstdio> + +///////////////////////////////////////////////////////////////////////// +// Debug/Info/Warning macros. +#define MSGMODULEFOO "(media plugin)" +#define STDERRMSG(...) do{\ + fprintf(stderr, MSGMODULEFOO " %s:%d: ", __FUNCTION__, __LINE__);\ + fprintf(stderr, __VA_ARGS__);\ + fputc('\n',stderr);\ + }while(0) +#define NULLMSG(...) do{}while(0) + +#define DEBUGMSG NULLMSG +#define INFOMSG STDERRMSG +#define WARNMSG STDERRMSG +///////////////////////////////////////////////////////////////////////// + +#endif /* __LLMEDIAIMPLGSTREAMERTRIVIALLOGGING_H__ */ diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp new file mode 100644 index 0000000000..d8ccfaa702 --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.cpp @@ -0,0 +1,532 @@ +/** + * @file llmediaimplgstreamervidplug.h + * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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$ + */ + +#if LL_GSTREAMER010_ENABLED + +#include "linden_common.h" + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/video/gstvideosink.h> + +#include "llmediaimplgstreamer_syms.h" +#include "llmediaimplgstreamertriviallogging.h" + +#include "llmediaimplgstreamervidplug.h" + + +GST_DEBUG_CATEGORY_STATIC (gst_slvideo_debug); +#define GST_CAT_DEFAULT gst_slvideo_debug + + +#define SLV_SIZECAPS ", width=(int)[1,2048], height=(int)[1,2048] " +#define SLV_ALLCAPS GST_VIDEO_CAPS_RGBx SLV_SIZECAPS + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ( + "sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS (SLV_ALLCAPS) + ); + +GST_BOILERPLATE (GstSLVideo, gst_slvideo, GstVideoSink, + GST_TYPE_VIDEO_SINK); + +static void gst_slvideo_set_property (GObject * object, guint prop_id, + const GValue * value, + GParamSpec * pspec); +static void gst_slvideo_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +gst_slvideo_base_init (gpointer gclass) +{ + static GstElementDetails element_details = { + (gchar*)"PluginTemplate", + (gchar*)"Generic/PluginTemplate", + (gchar*)"Generic Template Element", + (gchar*)"Linden Lab" + }; + GstElementClass *element_class = GST_ELEMENT_CLASS (gclass); + + llgst_element_class_add_pad_template (element_class, + llgst_static_pad_template_get (&sink_factory)); + llgst_element_class_set_details (element_class, &element_details); +} + + +static void +gst_slvideo_finalize (GObject * object) +{ + GstSLVideo *slvideo; + slvideo = GST_SLVIDEO (object); + if (slvideo->caps) + { + llgst_caps_unref(slvideo->caps); + } + + G_OBJECT_CLASS(parent_class)->finalize (object); +} + + +static GstFlowReturn +gst_slvideo_show_frame (GstBaseSink * bsink, GstBuffer * buf) +{ + GstSLVideo *slvideo; + llg_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); + + slvideo = GST_SLVIDEO(bsink); + +#if 0 + fprintf(stderr, "\n\ntransferring a frame of %dx%d <- %p (%d)\n\n", + slvideo->width, slvideo->height, GST_BUFFER_DATA(buf), + slvideo->format); +#endif + if (GST_BUFFER_DATA(buf)) + { + // copy frame and frame info into neutral territory + GST_OBJECT_LOCK(slvideo); + slvideo->retained_frame_ready = TRUE; + slvideo->retained_frame_width = slvideo->width; + slvideo->retained_frame_height = slvideo->height; + slvideo->retained_frame_format = slvideo->format; + int rowbytes = + SLVPixelFormatBytes[slvideo->retained_frame_format] * + slvideo->retained_frame_width; + int needbytes = rowbytes * slvideo->retained_frame_width; + // resize retained frame hunk only if necessary + if (needbytes != slvideo->retained_frame_allocbytes) + { + delete[] slvideo->retained_frame_data; + slvideo->retained_frame_data = new unsigned char[needbytes]; + slvideo->retained_frame_allocbytes = needbytes; + + } + // copy the actual frame data to neutral territory - + // flipped, for GL reasons + for (int ypos=0; ypos<slvideo->height; ++ypos) + { + memcpy(&slvideo->retained_frame_data[(slvideo->height-1-ypos)*rowbytes], + &(((unsigned char*)GST_BUFFER_DATA(buf))[ypos*rowbytes]), + rowbytes); + } + // done with the shared data + GST_OBJECT_UNLOCK(slvideo); + } + + return GST_FLOW_OK; +} + + +static GstStateChangeReturn +gst_slvideo_change_state(GstElement * element, GstStateChange transition) +{ + GstSLVideo *slvideo; + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + slvideo = GST_SLVIDEO (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret == GST_STATE_CHANGE_FAILURE) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + slvideo->fps_n = 0; + slvideo->fps_d = 1; + GST_VIDEO_SINK_WIDTH(slvideo) = 0; + GST_VIDEO_SINK_HEIGHT(slvideo) = 0; + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + + return ret; +} + + +static GstCaps * +gst_slvideo_get_caps (GstBaseSink * bsink) +{ + GstSLVideo *slvideo; + slvideo = GST_SLVIDEO(bsink); + + return llgst_caps_ref (slvideo->caps); +} + + +/* this function handles the link with other elements */ +static gboolean +gst_slvideo_set_caps (GstBaseSink * bsink, GstCaps * caps) +{ + GstSLVideo *filter; + GstStructure *structure; + + GST_DEBUG ("set caps with %" GST_PTR_FORMAT, caps); + + filter = GST_SLVIDEO(bsink); + + int width, height; + gboolean ret; + const GValue *fps; + const GValue *par; + structure = llgst_caps_get_structure (caps, 0); + ret = llgst_structure_get_int (structure, "width", &width); + ret = ret && llgst_structure_get_int (structure, "height", &height); + fps = llgst_structure_get_value (structure, "framerate"); + ret = ret && (fps != NULL); + par = llgst_structure_get_value (structure, "pixel-aspect-ratio"); + if (!ret) + return FALSE; + + INFOMSG("** filter caps set with width=%d, height=%d", width, height); + + GST_OBJECT_LOCK(filter); + + filter->width = width; + filter->height = height; + + filter->fps_n = llgst_value_get_fraction_numerator(fps); + filter->fps_d = llgst_value_get_fraction_denominator(fps); + if (par) + { + filter->par_n = llgst_value_get_fraction_numerator(par); + filter->par_d = llgst_value_get_fraction_denominator(par); + } + else + { + filter->par_n = 1; + filter->par_d = 1; + } + GST_VIDEO_SINK_WIDTH(filter) = width; + GST_VIDEO_SINK_HEIGHT(filter) = height; + + // crufty lump - we *always* accept *only* RGBX now. + /* + filter->format = SLV_PF_UNKNOWN; + if (0 == strcmp(llgst_structure_get_name(structure), + "video/x-raw-rgb")) + { + int red_mask; + int green_mask; + int blue_mask; + llgst_structure_get_int(structure, "red_mask", &red_mask); + llgst_structure_get_int(structure, "green_mask", &green_mask); + llgst_structure_get_int(structure, "blue_mask", &blue_mask); + if ((unsigned int)red_mask == 0xFF000000 && + (unsigned int)green_mask == 0x00FF0000 && + (unsigned int)blue_mask == 0x0000FF00) + { + filter->format = SLV_PF_RGBX; + //fprintf(stderr, "\n\nPIXEL FORMAT RGB\n\n"); + } else if ((unsigned int)red_mask == 0x0000FF00 && + (unsigned int)green_mask == 0x00FF0000 && + (unsigned int)blue_mask == 0xFF000000) + { + filter->format = SLV_PF_BGRX; + //fprintf(stderr, "\n\nPIXEL FORMAT BGR\n\n"); + } + }*/ + + filter->format = SLV_PF_RGBX; + + GST_OBJECT_UNLOCK(filter); + + return TRUE; +} + + +static gboolean +gst_slvideo_start (GstBaseSink * bsink) +{ + GstSLVideo *slvideo; + gboolean ret = TRUE; + + slvideo = GST_SLVIDEO(bsink); + + return ret; +} + +static gboolean +gst_slvideo_stop (GstBaseSink * bsink) +{ + GstSLVideo *slvideo; + slvideo = GST_SLVIDEO(bsink); + + // free-up retained frame buffer + GST_OBJECT_LOCK(slvideo); + slvideo->retained_frame_ready = FALSE; + delete[] slvideo->retained_frame_data; + slvideo->retained_frame_data = NULL; + slvideo->retained_frame_allocbytes = 0; + GST_OBJECT_UNLOCK(slvideo); + + return TRUE; +} + + +static GstFlowReturn +gst_slvideo_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + gint width, height; + GstStructure *structure = NULL; + GstSLVideo *slvideo; + slvideo = GST_SLVIDEO(bsink); + + // caps == requested caps + // we can ignore these and reverse-negotiate our preferred dimensions with + // the peer if we like - we need to do this to obey dynamic resize requests + // flowing in from the app. + structure = llgst_caps_get_structure (caps, 0); + if (!llgst_structure_get_int(structure, "width", &width) || + !llgst_structure_get_int(structure, "height", &height)) + { + GST_WARNING_OBJECT (slvideo, "no width/height in caps %" GST_PTR_FORMAT, caps); + return GST_FLOW_NOT_NEGOTIATED; + } + + GstBuffer *newbuf = llgst_buffer_new(); + bool made_bufferdata_ptr = false; +#define MAXDEPTHHACK 4 + + GST_OBJECT_LOCK(slvideo); + if (slvideo->resize_forced) + { + gint slwantwidth, slwantheight; + slwantwidth = slvideo->resize_try_width; + slwantheight = slvideo->resize_try_height; + + if (slwantwidth != width || + slwantheight != height) + { + // don't like requested caps, we will issue our own suggestion - copy + // the requested caps but substitute our own width and height and see + // if our peer is happy with that. + + GstCaps *desired_caps; + GstStructure *desired_struct; + desired_caps = llgst_caps_copy (caps); + desired_struct = llgst_caps_get_structure (desired_caps, 0); + + GValue value = {0}; + g_value_init(&value, G_TYPE_INT); + g_value_set_int(&value, slwantwidth); + llgst_structure_set_value (desired_struct, "width", &value); + g_value_unset(&value); + g_value_init(&value, G_TYPE_INT); + g_value_set_int(&value, slwantheight); + llgst_structure_set_value (desired_struct, "height", &value); + + if (llgst_pad_peer_accept_caps (GST_VIDEO_SINK_PAD (slvideo), + desired_caps)) + { + // todo: re-use buffers from a pool? + // todo: set MALLOCDATA to null, set DATA to point straight to shm? + + // peer likes our cap suggestion + DEBUGMSG("peer loves us :)"); + GST_BUFFER_SIZE(newbuf) = slwantwidth * slwantheight * MAXDEPTHHACK; + GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); + GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); + llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), desired_caps); + + made_bufferdata_ptr = true; + } else { + // peer hates our cap suggestion + INFOMSG("peer hates us :("); + llgst_caps_unref(desired_caps); + } + } + } + + if (!made_bufferdata_ptr) // need to fallback to malloc at original size + { + GST_BUFFER_SIZE(newbuf) = width * height * MAXDEPTHHACK; + GST_BUFFER_MALLOCDATA(newbuf) = (guint8*)g_malloc(GST_BUFFER_SIZE(newbuf)); + GST_BUFFER_DATA(newbuf) = GST_BUFFER_MALLOCDATA(newbuf); + llgst_buffer_set_caps (GST_BUFFER_CAST(newbuf), caps); + } + + GST_OBJECT_UNLOCK(slvideo); + + *buf = GST_BUFFER_CAST(newbuf); + + return GST_FLOW_OK; +} + + +/* initialize the plugin's class */ +static void +gst_slvideo_class_init (GstSLVideoClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSinkClass *gstbasesink_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesink_class = (GstBaseSinkClass *) klass; + + gobject_class->finalize = gst_slvideo_finalize; + gobject_class->set_property = gst_slvideo_set_property; + gobject_class->get_property = gst_slvideo_get_property; + + gstelement_class->change_state = gst_slvideo_change_state; + +#define LLGST_DEBUG_FUNCPTR(p) (p) + gstbasesink_class->get_caps = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_caps); + gstbasesink_class->set_caps = LLGST_DEBUG_FUNCPTR( gst_slvideo_set_caps); + gstbasesink_class->buffer_alloc=LLGST_DEBUG_FUNCPTR(gst_slvideo_buffer_alloc); + //gstbasesink_class->get_times = LLGST_DEBUG_FUNCPTR (gst_slvideo_get_times); + gstbasesink_class->preroll = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame); + gstbasesink_class->render = LLGST_DEBUG_FUNCPTR (gst_slvideo_show_frame); + + gstbasesink_class->start = LLGST_DEBUG_FUNCPTR (gst_slvideo_start); + gstbasesink_class->stop = LLGST_DEBUG_FUNCPTR (gst_slvideo_stop); + + // gstbasesink_class->unlock = LLGST_DEBUG_FUNCPTR (gst_slvideo_unlock); +#undef LLGST_DEBUG_FUNCPTR +} + + +/* initialize the new element + * instantiate pads and add them to element + * set functions + * initialize structure + */ +static void +gst_slvideo_init (GstSLVideo * filter, + GstSLVideoClass * gclass) +{ + filter->caps = NULL; + filter->width = -1; + filter->height = -1; + + // this is the info we share with the client app + GST_OBJECT_LOCK(filter); + filter->retained_frame_ready = FALSE; + filter->retained_frame_data = NULL; + filter->retained_frame_allocbytes = 0; + filter->retained_frame_width = filter->width; + filter->retained_frame_height = filter->height; + filter->retained_frame_format = SLV_PF_UNKNOWN; + GstCaps *caps = llgst_caps_from_string (SLV_ALLCAPS); + llgst_caps_replace (&filter->caps, caps); + filter->resize_forced = false; + filter->resize_try_width = -1; + filter->resize_try_height = -1; + GST_OBJECT_UNLOCK(filter); +} + +static void +gst_slvideo_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + llg_return_if_fail (GST_IS_SLVIDEO (object)); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_slvideo_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + llg_return_if_fail (GST_IS_SLVIDEO (object)); + + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +/* entry point to initialize the plug-in + * initialize the plug-in itself + * register the element factories and pad templates + * register the features + */ +static gboolean +plugin_init (GstPlugin * plugin) +{ + DEBUGMSG("\n\n\nPLUGIN INIT\n\n\n"); + + GST_DEBUG_CATEGORY_INIT (gst_slvideo_debug, (gchar*)"private-slvideo-plugin", + 0, (gchar*)"Second Life Video Sink"); + + return llgst_element_register (plugin, "private-slvideo", + GST_RANK_NONE, GST_TYPE_SLVIDEO); +} + +/* this is the structure that gstreamer looks for to register plugins + */ +/* NOTE: Can't rely upon GST_PLUGIN_DEFINE_STATIC to self-register, since + some g++ versions buggily avoid __attribute__((constructor)) functions - + so we provide an explicit plugin init function. + */ +void gst_slvideo_init_class (void) +{ +#define PACKAGE "packagehack" + // this macro quietly refers to PACKAGE internally + static GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "private-slvideoplugin", + "SL Video sink plugin", + plugin_init, "0.1", GST_LICENSE_UNKNOWN, + "Second Life", + "http://www.secondlife.com/"); +#undef PACKAGE + ll_gst_plugin_register_static (&gst_plugin_desc); + DEBUGMSG(stderr, "\n\n\nCLASS INIT\n\n\n"); +} + +#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h new file mode 100644 index 0000000000..f6d55b8758 --- /dev/null +++ b/indra/media_plugins/gstreamer010/llmediaimplgstreamervidplug.h @@ -0,0 +1,109 @@ +/** + * @file llmediaimplgstreamervidplug.h + * @brief Video-consuming static GStreamer plugin for gst-to-LLMediaImpl + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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 __GST_SLVIDEO_H__ +#define __GST_SLVIDEO_H__ + +#if LL_GSTREAMER010_ENABLED + +extern "C" { +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/video/gstvideosink.h> +} + +G_BEGIN_DECLS + +/* #defines don't like whitespacey bits */ +#define GST_TYPE_SLVIDEO \ + (gst_slvideo_get_type()) +#define GST_SLVIDEO(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_SLVIDEO,GstSLVideo)) +#define GST_SLVIDEO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_SLVIDEO,GstSLVideoClass)) +#define GST_IS_SLVIDEO(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_SLVIDEO)) +#define GST_IS_SLVIDEO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_SLVIDEO)) + +typedef struct _GstSLVideo GstSLVideo; +typedef struct _GstSLVideoClass GstSLVideoClass; + +typedef enum { + SLV_PF_UNKNOWN = 0, + SLV_PF_RGBX = 1, + SLV_PF_BGRX = 2, + SLV__END = 3 +} SLVPixelFormat; +const int SLVPixelFormatBytes[SLV__END] = {1, 4, 4}; + +struct _GstSLVideo +{ + GstVideoSink video_sink; + + GstCaps *caps; + + int fps_n, fps_d; + int par_n, par_d; + int height, width; + SLVPixelFormat format; + + // SHARED WITH APPLICATION: + // Access to the following should be protected by GST_OBJECT_LOCK() on + // the GstSLVideo object, and should be totally consistent upon UNLOCK + // (i.e. all written at once to reflect the current retained frame info + // when the retained frame is updated.) + bool retained_frame_ready; // new frame ready since flag last reset. (*TODO: could get the writer to wait on a semaphore instead of having the reader poll, potentially making dropped frames somewhat cheaper.) + unsigned char* retained_frame_data; + int retained_frame_allocbytes; + int retained_frame_width, retained_frame_height; + SLVPixelFormat retained_frame_format; + // sticky resize info + bool resize_forced; + int resize_try_width; + int resize_try_height; +}; + +struct _GstSLVideoClass +{ + GstVideoSinkClass parent_class; +}; + +GType gst_slvideo_get_type (void); + +void gst_slvideo_init_class (void); + +G_END_DECLS + +#endif // LL_GSTREAMER010_ENABLED + +#endif /* __GST_SLVIDEO_H__ */ diff --git a/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp b/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp new file mode 100644 index 0000000000..647db7a5bf --- /dev/null +++ b/indra/media_plugins/gstreamer010/media_plugin_gstreamer010.cpp @@ -0,0 +1,1204 @@ +/** + * @file media_plugin_gstreamer010.cpp + * @brief GStreamer-0.10 plugin for LLMedia API plugin system + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llgl.h" + +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#if LL_GSTREAMER010_ENABLED + +extern "C" { +#include <gst/gst.h> +} + +#include "llmediaimplgstreamer.h" +#include "llmediaimplgstreamertriviallogging.h" + +#include "llmediaimplgstreamervidplug.h" + +#include "llmediaimplgstreamer_syms.h" + +////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginGStreamer010 : public MediaPluginBase +{ +public: + MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginGStreamer010(); + + /* virtual */ void receiveMessage(const char *message_string); + + static bool startup(); + static bool closedown(); + + gboolean processGSTEvents(GstBus *bus, + GstMessage *message); + +private: + std::string getVersion(); + bool navigateTo( const std::string urlIn ); + bool seek( double time_sec ); + bool setVolume( float volume ); + + // misc + bool pause(); + bool stop(); + bool play(double rate); + bool getTimePos(double &sec_out); + + static const double MIN_LOOP_SEC = 1.0F; + + bool mIsLooping; + + enum ECommand { + COMMAND_NONE, + COMMAND_STOP, + COMMAND_PLAY, + COMMAND_FAST_FORWARD, + COMMAND_FAST_REWIND, + COMMAND_PAUSE, + COMMAND_SEEK, + }; + ECommand mCommand; + +private: + bool unload(); + bool load(); + + bool update(int milliseconds); + void mouseDown( int x, int y ); + void mouseUp( int x, int y ); + void mouseMove( int x, int y ); + + bool sizeChanged(); + + static bool mDoneInit; + + guint mBusWatchID; + + float mVolume; + + int mDepth; + + // media natural size + int mNaturalWidth; + int mNaturalHeight; + int mNaturalRowbytes; + // previous media natural size so we can detect changes + int mPreviousNaturalWidth; + int mPreviousNaturalHeight; + // desired render size from host + int mWidth; + int mHeight; + // padded texture size we need to write into + int mTextureWidth; + int mTextureHeight; + + int mTextureFormatPrimary; + int mTextureFormatType; + + bool mSeekWanted; + double mSeekDestination; + + // Very GStreamer-specific + GMainLoop *mPump; // event pump for this media + GstElement *mPlaybin; + GstSLVideo *mVideoSink; +}; + +//static +bool MediaPluginGStreamer010::mDoneInit = false; + +MediaPluginGStreamer010::MediaPluginGStreamer010( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) : + MediaPluginBase(host_send_func, host_user_data), + mBusWatchID ( 0 ), + mNaturalRowbytes ( 4 ), + mTextureFormatPrimary ( GL_RGBA ), + mTextureFormatType ( GL_UNSIGNED_INT_8_8_8_8_REV ), + mSeekWanted(false), + mSeekDestination(0.0), + mPump ( NULL ), + mPlaybin ( NULL ), + mVideoSink ( NULL ), + mCommand ( COMMAND_NONE ) +{ + std::ostringstream str; + INFOMSG("MediaPluginGStreamer010 constructor - my PID=%u", U32(getpid())); +} + +/////////////////////////////////////////////////////////////////////////////// +// +//#define LL_GST_REPORT_STATE_CHANGES +#ifdef LL_GST_REPORT_STATE_CHANGES +static char* get_gst_state_name(GstState state) +{ + switch (state) { + case GST_STATE_VOID_PENDING: return "VOID_PENDING"; + case GST_STATE_NULL: return "NULL"; + case GST_STATE_READY: return "READY"; + case GST_STATE_PAUSED: return "PAUSED"; + case GST_STATE_PLAYING: return "PLAYING"; + } + return "(unknown)"; +} +#endif // LL_GST_REPORT_STATE_CHANGES + +gboolean +MediaPluginGStreamer010::processGSTEvents(GstBus *bus, + GstMessage *message) +{ + if (!message) + return TRUE; // shield against GStreamer bug + + if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_STATE_CHANGED && + GST_MESSAGE_TYPE(message) != GST_MESSAGE_BUFFERING) + { + DEBUGMSG("Got GST message type: %s", + LLGST_MESSAGE_TYPE_NAME (message)); + } + else + { + DEBUGMSG("Got GST message type: %s", + LLGST_MESSAGE_TYPE_NAME (message)); + } + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_BUFFERING: { + // NEEDS GST 0.10.11+ + if (llgst_message_parse_buffering) + { + gint percent = 0; + llgst_message_parse_buffering(message, &percent); + DEBUGMSG("GST buffering: %d%%", percent); + } + break; + } + case GST_MESSAGE_STATE_CHANGED: { + GstState old_state; + GstState new_state; + GstState pending_state; + llgst_message_parse_state_changed(message, + &old_state, + &new_state, + &pending_state); +#ifdef LL_GST_REPORT_STATE_CHANGES + // not generally very useful, and rather spammy. + DEBUGMSG("state change (old,<new>,pending): %s,<%s>,%s", + get_gst_state_name(old_state), + get_gst_state_name(new_state), + get_gst_state_name(pending_state)); +#endif // LL_GST_REPORT_STATE_CHANGES + + switch (new_state) { + case GST_STATE_VOID_PENDING: + break; + case GST_STATE_NULL: + break; + case GST_STATE_READY: + setStatus(STATUS_LOADED); + break; + case GST_STATE_PAUSED: + setStatus(STATUS_PAUSED); + break; + case GST_STATE_PLAYING: + setStatus(STATUS_PLAYING); + break; + } + break; + } + case GST_MESSAGE_ERROR: { + GError *err = NULL; + gchar *debug = NULL; + + llgst_message_parse_error (message, &err, &debug); + WARNMSG("GST error: %s", err?err->message:"(unknown)"); + if (err) + g_error_free (err); + g_free (debug); + + mCommand = COMMAND_STOP; + + setStatus(STATUS_ERROR); + + break; + } + case GST_MESSAGE_INFO: { + if (llgst_message_parse_info) + { + GError *err = NULL; + gchar *debug = NULL; + + llgst_message_parse_info (message, &err, &debug); + INFOMSG("GST info: %s", err?err->message:"(unknown)"); + if (err) + g_error_free (err); + g_free (debug); + } + break; + } + case GST_MESSAGE_WARNING: { + GError *err = NULL; + gchar *debug = NULL; + + llgst_message_parse_warning (message, &err, &debug); + WARNMSG("GST warning: %s", err?err->message:"(unknown)"); + if (err) + g_error_free (err); + g_free (debug); + + break; + } + case GST_MESSAGE_EOS: + /* end-of-stream */ + DEBUGMSG("GST end-of-stream."); + if (mIsLooping) + { + DEBUGMSG("looping media..."); + double eos_pos_sec = 0.0F; + bool got_eos_position = getTimePos(eos_pos_sec); + + if (got_eos_position && eos_pos_sec < MIN_LOOP_SEC) + { + // if we know that the movie is really short, don't + // loop it else it can easily become a time-hog + // because of GStreamer spin-up overhead + DEBUGMSG("really short movie (%0.3fsec) - not gonna loop this, pausing instead.", eos_pos_sec); + // inject a COMMAND_PAUSE + mCommand = COMMAND_PAUSE; + } + else + { +#undef LLGST_LOOP_BY_SEEKING +// loop with a stop-start instead of a seek, because it actually seems rather +// faster than seeking on remote streams. +#ifdef LLGST_LOOP_BY_SEEKING + // first, try looping by an explicit rewind + bool seeksuccess = seek(0.0); + if (seeksuccess) + { + play(1.0); + } + else +#endif // LLGST_LOOP_BY_SEEKING + { // use clumsy stop-start to loop + DEBUGMSG("didn't loop by rewinding - stopping and starting instead..."); + stop(); + play(1.0); + } + } + } + else // not a looping media + { + // inject a COMMAND_STOP + mCommand = COMMAND_STOP; + } + break; + default: + /* unhandled message */ + break; + } + + /* we want to be notified again the next time there is a message + * on the bus, so return true (false means we want to stop watching + * for messages on the bus and our callback should not be called again) + */ + return TRUE; +} + +extern "C" { +gboolean +llmediaimplgstreamer_bus_callback (GstBus *bus, + GstMessage *message, + gpointer data) +{ + MediaPluginGStreamer010 *impl = (MediaPluginGStreamer010*)data; + return impl->processGSTEvents(bus, message); +} +} // extern "C" + + + +bool +MediaPluginGStreamer010::navigateTo ( const std::string urlIn ) +{ + if (!mDoneInit) + return false; // error + + setStatus(STATUS_LOADING); + + DEBUGMSG("Setting media URI: %s", urlIn.c_str()); + + mSeekWanted = false; + + if (NULL == mPump || + NULL == mPlaybin) + { + setStatus(STATUS_ERROR); + return false; // error + } + + // set URI + g_object_set (G_OBJECT (mPlaybin), "uri", urlIn.c_str(), NULL); + //g_object_set (G_OBJECT (mPlaybin), "uri", "file:///tmp/movie", NULL); + + // navigateTo implicitly plays, too. + play(1.0); + + return true; +} + + +bool +MediaPluginGStreamer010::update(int milliseconds) +{ + if (!mDoneInit) + return false; // error + + DEBUGMSG("updating media..."); + + // sanity check + if (NULL == mPump || + NULL == mPlaybin) + { + DEBUGMSG("dead media..."); + return false; + } + + // see if there's an outstanding seek wanted + if (mSeekWanted && + // bleh, GST has to be happy that the movie is really truly playing + // or it may quietly ignore the seek (with rtsp:// at least). + (GST_STATE(mPlaybin) == GST_STATE_PLAYING)) + { + seek(mSeekDestination); + mSeekWanted = false; + } + + // *TODO: time-limit - but there isn't a lot we can do here, most + // time is spent in gstreamer's own opaque worker-threads. maybe + // we can do something sneaky like only unlock the video object + // for 'milliseconds' and otherwise hold the lock. + while (g_main_context_pending(g_main_loop_get_context(mPump))) + { + g_main_context_iteration(g_main_loop_get_context(mPump), FALSE); + } + + // check for availability of a new frame + + if (mVideoSink) + { + GST_OBJECT_LOCK(mVideoSink); + if (mVideoSink->retained_frame_ready) + { + DEBUGMSG("NEW FRAME READY"); + + if (mVideoSink->retained_frame_width != mNaturalWidth || + mVideoSink->retained_frame_height != mNaturalHeight) + // *TODO: also check for change in format + { + // just resize container, don't consume frame + int neww = mVideoSink->retained_frame_width; + int newh = mVideoSink->retained_frame_height; + + int newd = 4; + mTextureFormatPrimary = GL_RGBA; + mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV; + + /* + int newd = SLVPixelFormatBytes[mVideoSink->retained_frame_format]; + if (SLV_PF_BGRX == mVideoSink->retained_frame_format) + { + mTextureFormatPrimary = GL_BGRA; + mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV; + } + else + { + mTextureFormatPrimary = GL_RGBA; + mTextureFormatType = GL_UNSIGNED_INT_8_8_8_8_REV; + } + */ + + GST_OBJECT_UNLOCK(mVideoSink); + + mNaturalRowbytes = neww * newd; + DEBUGMSG("video container resized to %dx%d", + neww, newh); + + mDepth = newd; + mNaturalWidth = neww; + mNaturalHeight = newh; + sizeChanged(); + return true; + } + + if (mPixels && + mNaturalHeight <= mHeight && + mNaturalWidth <= mWidth && + !mTextureSegmentName.empty()) + { + + // we're gonna totally consume this frame - reset 'ready' flag + mVideoSink->retained_frame_ready = FALSE; + int destination_rowbytes = mWidth * mDepth; + for (int row=0; row<mNaturalHeight; ++row) + { + memcpy(&mPixels + [destination_rowbytes * row], + &mVideoSink->retained_frame_data + [mNaturalRowbytes * row], + mNaturalRowbytes); + } + + GST_OBJECT_UNLOCK(mVideoSink); + DEBUGMSG("NEW FRAME REALLY TRULY CONSUMED, TELLING HOST"); + + setDirty(0,0,mNaturalWidth,mNaturalHeight); + } + else + { + // new frame ready, but we're not ready to + // consume it. + + GST_OBJECT_UNLOCK(mVideoSink); + + DEBUGMSG("NEW FRAME not consumed, still waiting for a shm segment and/or shm resize"); + } + + return true; + } + else + { + // nothing to do yet. + GST_OBJECT_UNLOCK(mVideoSink); + return true; + } + } + + return true; +} + + +void +MediaPluginGStreamer010::mouseDown( int x, int y ) +{ + // do nothing +} + +void +MediaPluginGStreamer010::mouseUp( int x, int y ) +{ + // do nothing +} + +void +MediaPluginGStreamer010::mouseMove( int x, int y ) +{ + // do nothing +} + + +bool +MediaPluginGStreamer010::pause() +{ + DEBUGMSG("pausing media..."); + // todo: error-check this? + llgst_element_set_state(mPlaybin, GST_STATE_PAUSED); + return true; +} + +bool +MediaPluginGStreamer010::stop() +{ + DEBUGMSG("stopping media..."); + // todo: error-check this? + llgst_element_set_state(mPlaybin, GST_STATE_READY); + return true; +} + +bool +MediaPluginGStreamer010::play(double rate) +{ + // NOTE: we don't actually support non-natural rate. + + DEBUGMSG("playing media... rate=%f", rate); + // todo: error-check this? + llgst_element_set_state(mPlaybin, GST_STATE_PLAYING); + return true; +} + +bool +MediaPluginGStreamer010::setVolume( float volume ) +{ + // we try to only update volume as conservatively as + // possible, as many gst-plugins-base versions up to at least + // November 2008 have critical race-conditions in setting volume - sigh + if (mVolume == volume) + return true; // nothing to do, everything's fine + + mVolume = volume; + if (mDoneInit && mPlaybin) + { + g_object_set(mPlaybin, "volume", mVolume, NULL); + return true; + } + + return false; +} + +bool +MediaPluginGStreamer010::seek(double time_sec) +{ + bool success = false; + if (mDoneInit && mPlaybin) + { + success = llgst_element_seek(mPlaybin, 1.0F, GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_FLUSH | + GST_SEEK_FLAG_KEY_UNIT), + GST_SEEK_TYPE_SET, gint64(time_sec*GST_SECOND), + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); + } + DEBUGMSG("MEDIA SEEK REQUEST to %fsec result was %d", + float(time_sec), int(success)); + return success; +} + +bool +MediaPluginGStreamer010::getTimePos(double &sec_out) +{ + bool got_position = false; + if (mPlaybin) + { + gint64 pos; + GstFormat timefmt = GST_FORMAT_TIME; + got_position = + llgst_element_query_position && + llgst_element_query_position(mPlaybin, + &timefmt, + &pos); + got_position = got_position + && (timefmt == GST_FORMAT_TIME); + // GStreamer may have other ideas, but we consider the current position + // undefined if not PLAYING or PAUSED + got_position = got_position && + (GST_STATE(mPlaybin) == GST_STATE_PLAYING || + GST_STATE(mPlaybin) == GST_STATE_PAUSED); + if (got_position && !GST_CLOCK_TIME_IS_VALID(pos)) + { + if (GST_STATE(mPlaybin) == GST_STATE_PLAYING) + { + // if we're playing then we treat an invalid clock time + // as 0, for complicated reasons (insert reason here) + pos = 0; + } + else + { + got_position = false; + } + + } + // If all the preconditions succeeded... we can trust the result. + if (got_position) + { + sec_out = double(pos) / double(GST_SECOND); // gst to sec + } + } + return got_position; +} + +bool +MediaPluginGStreamer010::load() +{ + if (!mDoneInit) + return false; // error + + setStatus(STATUS_LOADING); + + DEBUGMSG("setting up media..."); + + mIsLooping = false; + mVolume = 0.1234567; // minor hack to force an initial volume update + + // Create a pumpable main-loop for this media + mPump = g_main_loop_new (NULL, FALSE); + if (!mPump) + { + setStatus(STATUS_ERROR); + return false; // error + } + + // instantiate a playbin element to do the hard work + mPlaybin = llgst_element_factory_make ("playbin", "play"); + if (!mPlaybin) + { + setStatus(STATUS_ERROR); + return false; // error + } + + // get playbin's bus + GstBus *bus = llgst_pipeline_get_bus (GST_PIPELINE (mPlaybin)); + if (!bus) + { + setStatus(STATUS_ERROR); + return false; // error + } + mBusWatchID = llgst_bus_add_watch (bus, + llmediaimplgstreamer_bus_callback, + this); + llgst_object_unref (bus); + + if (NULL == getenv("LL_GSTREAMER_EXTERNAL")) { + // instantiate a custom video sink + mVideoSink = + GST_SLVIDEO(llgst_element_factory_make ("private-slvideo", "slvideo")); + if (!mVideoSink) + { + WARNMSG("Could not instantiate private-slvideo element."); + // todo: cleanup. + setStatus(STATUS_ERROR); + return false; // error + } + + // connect the pieces + g_object_set(mPlaybin, "video-sink", mVideoSink, NULL); + } + + return true; +} + +bool +MediaPluginGStreamer010::unload () +{ + if (!mDoneInit) + return false; // error + + DEBUGMSG("unloading media..."); + + // stop getting callbacks for this bus + g_source_remove(mBusWatchID); + mBusWatchID = 0; + + if (mPlaybin) + { + llgst_element_set_state (mPlaybin, GST_STATE_NULL); + llgst_object_unref (GST_OBJECT (mPlaybin)); + mPlaybin = NULL; + } + + if (mPump) + { + g_main_loop_quit(mPump); + mPump = NULL; + } + + mVideoSink = NULL; + + setStatus(STATUS_NONE); + + return true; +} + + +//static +bool +MediaPluginGStreamer010::startup() +{ + // first - check if GStreamer is explicitly disabled + if (NULL != getenv("LL_DISABLE_GSTREAMER")) + return false; + + // only do global GStreamer initialization once. + if (!mDoneInit) + { + g_thread_init(NULL); + + // Init the glib type system - we need it. + g_type_init(); + + // Get symbols! +#if LL_DARWIN + if (! grab_gst_syms("libgstreamer-0.10.dylib", + "libgstvideo-0.10.dylib") ) +#elseif LL_WINDOWS + if (! grab_gst_syms("libgstreamer-0.10.dll", + "libgstvideo-0.10.dll") ) +#else // linux or other ELFy unixoid + if (! grab_gst_syms("libgstreamer-0.10.so.0", + "libgstvideo-0.10.so.0") ) +#endif + { + WARNMSG("Couldn't find suitable GStreamer 0.10 support on this system - video playback disabled."); + return false; + } + + if (llgst_segtrap_set_enabled) + { + llgst_segtrap_set_enabled(FALSE); + } + else + { + WARNMSG("gst_segtrap_set_enabled() is not available; plugin crashes won't be caught."); + } + +#if LL_LINUX + // Gstreamer tries a fork during init, waitpid-ing on it, + // which conflicts with any installed SIGCHLD handler... + struct sigaction tmpact, oldact; + if (llgst_registry_fork_set_enabled) { + // if we can disable SIGCHLD-using forking behaviour, + // do it. + llgst_registry_fork_set_enabled(false); + } + else { + // else temporarily install default SIGCHLD handler + // while GStreamer initialises + tmpact.sa_handler = SIG_DFL; + sigemptyset( &tmpact.sa_mask ); + tmpact.sa_flags = SA_SIGINFO; + sigaction(SIGCHLD, &tmpact, &oldact); + } +#endif // LL_LINUX + + // Protect against GStreamer resetting the locale, yuck. + static std::string saved_locale; + saved_locale = setlocale(LC_ALL, NULL); + + // finally, try to initialize GStreamer! + GError *err = NULL; + gboolean init_gst_success = llgst_init_check(NULL, NULL, &err); + + // restore old locale + setlocale(LC_ALL, saved_locale.c_str() ); + +#if LL_LINUX + // restore old SIGCHLD handler + if (!llgst_registry_fork_set_enabled) + sigaction(SIGCHLD, &oldact, NULL); +#endif // LL_LINUX + + if (!init_gst_success) // fail + { + if (err) + { + WARNMSG("GST init failed: %s", err->message); + g_error_free(err); + } + else + { + WARNMSG("GST init failed for unspecified reason."); + } + return false; + } + + // Init our custom plugins - only really need do this once. + gst_slvideo_init_class(); + + mDoneInit = true; + } + + return true; +} + + +bool +MediaPluginGStreamer010::sizeChanged() +{ + // the shared writing space has possibly changed size/location/whatever + + // Check to see whether the movie's natural size has updated + if (mNaturalWidth != mPreviousNaturalWidth || + mNaturalHeight != mPreviousNaturalHeight) + { + mPreviousNaturalWidth = mNaturalWidth; + mPreviousNaturalHeight = mNaturalHeight; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); + message.setValue("name", mTextureSegmentName); + message.setValueS32("width", mNaturalWidth); + message.setValueS32("height", mNaturalHeight); + DEBUGMSG("<--- Sending size change request to application with name: '%s' - size is %d x %d", mTextureSegmentName.c_str(), mNaturalWidth, mNaturalHeight); + sendMessage(message); + } + + return true; +} + + + +//static +bool +MediaPluginGStreamer010::closedown() +{ + if (!mDoneInit) + return false; // error + + ungrab_gst_syms(); + + mDoneInit = false; + + return true; +} + +MediaPluginGStreamer010::~MediaPluginGStreamer010() +{ + DEBUGMSG("MediaPluginGStreamer010 destructor"); + + closedown(); + + DEBUGMSG("GStreamer010 closing down"); +} + + +std::string +MediaPluginGStreamer010::getVersion() +{ + std::string plugin_version = "GStreamer010 media plugin, GStreamer version "; + if (mDoneInit && + llgst_version) + { + guint major, minor, micro, nano; + llgst_version(&major, &minor, µ, &nano); + plugin_version += llformat("%u.%u.%u.%u (runtime), %u.%u.%u.%u (headers)", (unsigned int)major, (unsigned int)minor, (unsigned int)micro, (unsigned int)nano, (unsigned int)GST_VERSION_MAJOR, (unsigned int)GST_VERSION_MINOR, (unsigned int)GST_VERSION_MICRO, (unsigned int)GST_VERSION_NANO); + } + else + { + plugin_version += "(unknown)"; + } + return plugin_version; +} + +void MediaPluginGStreamer010::receiveMessage(const char *message_string) +{ + //std::cerr << "MediaPluginGStreamer010::receiveMessage: received message: \"" << message_string << "\"" << std::endl; + + LLPluginMessage message_in; + + if(message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if(message_name == "init") + { + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; + message.setValueLLSD("versions", versions); + + if ( load() ) + { + DEBUGMSG("GStreamer010 media instance set up"); + } + else + { + WARNMSG("GStreamer010 media instance failed to set up"); + } + + message.setValue("plugin_version", getVersion()); + sendMessage(message); + + // Plugin gets to decide the texture parameters to use. + message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + // lame to have to decide this now, it depends on the movie. Oh well. + mDepth = 4; + + mNaturalWidth = 1; + mNaturalHeight = 1; + mPreviousNaturalWidth = 1; + mPreviousNaturalHeight = 1; + mWidth = 1; + mHeight = 1; + mTextureWidth = 1; + mTextureHeight = 1; + + message.setValueU32("format", GL_RGBA); + message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV); + + message.setValueS32("depth", mDepth); + message.setValueS32("default_width", mWidth); + message.setValueS32("default_height", mHeight); + message.setValueU32("internalformat", GL_RGBA8); + message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. + message.setValueBoolean("allow_downsample", true); // we respond with grace and performance if asked to downscale + sendMessage(message); + } + else if(message_name == "idle") + { + // no response is necessary here. + double time = message_in.getValueReal("time"); + + // Convert time to milliseconds for update() + update((int)(time * 1000.0f)); + } + else if(message_name == "cleanup") + { + unload(); + closedown(); + } + else if(message_name == "shm_added") + { + SharedSegmentInfo info; + U64 address_lo = message_in.getValueU32("address"); + U64 address_hi = message_in.hasValue("address_1") ? message_in.getValueU32("address_1") : 0; + info.mAddress = (void*)((address_lo) | + (address_hi * (U64(1)<<31))); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); + + std::ostringstream str; + INFOMSG("MediaPluginGStreamer010::receiveMessage: shared memory added, name: %s, size: %d, address: %p", name.c_str(), int(info.mSize), info.mAddress); + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if(message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + + DEBUGMSG("MediaPluginGStreamer010::receiveMessage: shared memory remove, name = %s", name.c_str()); + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + if(mPixels == iter->second.mAddress) + { + // This is the currently active pixel buffer. Make sure we stop drawing to it. + mPixels = NULL; + mTextureSegmentName.clear(); + + // Make sure the movie decoder is no longer pointed at the shared segment. + sizeChanged(); + } + mSharedSegments.erase(iter); + } + else + { + WARNMSG("MediaPluginGStreamer010::receiveMessage: unknown shared memory region!"); + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { + std::ostringstream str; + INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown base message: %s", message_name.c_str()); + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if(message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + std::ostringstream str; + INFOMSG("---->Got size change instruction from application with shm name: %s - size is %d x %d", name.c_str(), width, height); + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + + if(!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + INFOMSG("*** Got size change with matching shm, new size is %d x %d", width, height); + INFOMSG("*** Got size change with matching shm, texture size size is %d x %d", texture_width, texture_height); + + mPixels = (unsigned char*)iter->second.mAddress; + mTextureSegmentName = name; + mWidth = width; + mHeight = height; + + if (texture_width > 1 || + texture_height > 1) // not a dummy size from the app, a real explicit forced size + { + INFOMSG("**** = REAL RESIZE REQUEST FROM APP"); + + GST_OBJECT_LOCK(mVideoSink); + mVideoSink->resize_forced = true; + mVideoSink->resize_try_width = texture_width; + mVideoSink->resize_try_height = texture_height; + GST_OBJECT_UNLOCK(mVideoSink); + } + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + } + } + } + else if(message_name == "load_uri") + { + std::string uri = message_in.getValue("uri"); + navigateTo( uri ); + sendStatus(); + } + else if(message_name == "mouse_event") + { + std::string event = message_in.getValue("event"); + S32 x = message_in.getValueS32("x"); + S32 y = message_in.getValueS32("y"); + + if(event == "down") + { + mouseDown(x, y); + } + else if(event == "up") + { + mouseUp(x, y); + } + else if(event == "move") + { + mouseMove(x, y); + }; + }; + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + if(message_name == "stop") + { + stop(); + } + else if(message_name == "start") + { + double rate = 0.0; + if(message_in.hasValue("rate")) + { + rate = message_in.getValueReal("rate"); + } + // NOTE: we don't actually support rate. + play(rate); + } + else if(message_name == "pause") + { + pause(); + } + else if(message_name == "seek") + { + double time = message_in.getValueReal("time"); + // defer the actual seek in case we haven't + // really truly started yet in which case there + // is nothing to seek upon + mSeekWanted = true; + mSeekDestination = time; + } + else if(message_name == "set_loop") + { + bool loop = message_in.getValueBoolean("loop"); + mIsLooping = loop; + } + else if(message_name == "set_volume") + { + double volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } + else + { + INFOMSG("MediaPluginGStreamer010::receiveMessage: unknown message class: %s", message_class.c_str()); + } + } +} + +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + if (MediaPluginGStreamer010::startup()) + { + MediaPluginGStreamer010 *self = new MediaPluginGStreamer010(host_send_func, host_user_data); + *plugin_send_func = MediaPluginGStreamer010::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; // okay + } + else + { + return -1; // failed to init + } +} + +#else // LL_GSTREAMER010_ENABLED + +// Stubbed-out class with constructor/destructor (necessary or windows linker +// will just think its dead code and optimize it all out) +class MediaPluginGStreamer010 : public MediaPluginBase +{ +public: + MediaPluginGStreamer010(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginGStreamer010(); + /* virtual */ void receiveMessage(const char *message_string); +}; + +MediaPluginGStreamer010::MediaPluginGStreamer010( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) : + MediaPluginBase(host_send_func, host_user_data) +{ + // no-op +} + +MediaPluginGStreamer010::~MediaPluginGStreamer010() +{ + // no-op +} + +void MediaPluginGStreamer010::receiveMessage(const char *message_string) +{ + // no-op +} + +// We're building without GStreamer enabled. Just refuse to initialize. +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + return -1; +} + +#endif // LL_GSTREAMER010_ENABLED diff --git a/indra/media_plugins/quicktime/CMakeLists.txt b/indra/media_plugins/quicktime/CMakeLists.txt new file mode 100644 index 0000000000..db11c9ae21 --- /dev/null +++ b/indra/media_plugins/quicktime/CMakeLists.txt @@ -0,0 +1,83 @@ +# -*- cmake -*- + +project(media_plugin_quicktime) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(MediaPluginBase) +include(FindOpenGL) +include(QuickTimePlugin) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} +) + +if (DARWIN) + include(CMakeFindFrameworks) + find_library(CARBON_LIBRARY Carbon) +endif (DARWIN) + + +### media_plugin_quicktime + +set(media_plugin_quicktime_SOURCE_FILES + media_plugin_quicktime.cpp + ) + +add_library(media_plugin_quicktime + SHARED + ${media_plugin_quicktime_SOURCE_FILES} +) + +target_link_libraries(media_plugin_quicktime + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${QUICKTIME_LIBRARY} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +add_dependencies(media_plugin_quicktime + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + +if (QUICKTIME) + + add_definitions(-DLL_QUICKTIME_ENABLED=1) + + if (DARWIN) + # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name + set_target_properties( + media_plugin_quicktime + PROPERTIES + PREFIX "" + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + ) + +# We use a bunch of deprecated system APIs. + set_source_files_properties( + media_plugin_quicktime.cpp PROPERTIES + COMPILE_FLAGS -Wno-deprecated-declarations + ) + find_library(CARBON_LIBRARY Carbon) + target_link_libraries(media_plugin_quicktime ${CARBON_LIBRARY}) + endif (DARWIN) +endif (QUICKTIME) + diff --git a/indra/media_plugins/quicktime/media_plugin_quicktime.cpp b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp new file mode 100644 index 0000000000..e9be458960 --- /dev/null +++ b/indra/media_plugins/quicktime/media_plugin_quicktime.cpp @@ -0,0 +1,984 @@ +/** + * @file media_plugin_quicktime.cpp + * @brief QuickTime plugin for LLMedia API plugin system + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llgl.h" + +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#if LL_QUICKTIME_ENABLED + +#if defined(LL_DARWIN) + #include <QuickTime/QuickTime.h> +#elif defined(LL_WINDOWS) + #include "MacTypes.h" + #include "QTML.h" + #include "Movies.h" + #include "QDoffscreen.h" + #include "FixMath.h" +#endif + +// TODO: Make sure that the only symbol exported from this library is LLPluginInitEntryPoint +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginQuickTime : public MediaPluginBase +{ +public: + MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginQuickTime(); + + /* virtual */ void receiveMessage(const char *message_string); + +private: + + int mNaturalWidth; + int mNaturalHeight; + Movie mMovieHandle; + GWorldPtr mGWorldHandle; + ComponentInstance mMovieController; + int mCurVolume; + bool mMediaSizeChanging; + bool mIsLooping; + const int mMinWidth; + const int mMaxWidth; + const int mMinHeight; + const int mMaxHeight; + F64 mPlayRate; + + enum ECommand { + COMMAND_NONE, + COMMAND_STOP, + COMMAND_PLAY, + COMMAND_FAST_FORWARD, + COMMAND_FAST_REWIND, + COMMAND_PAUSE, + COMMAND_SEEK, + }; + ECommand mCommand; + + // Override this to add current time and duration to the message + /*virtual*/ void setDirty(int left, int top, int right, int bottom) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "updated"); + + message.setValueS32("left", left); + message.setValueS32("top", top); + message.setValueS32("right", right); + message.setValueS32("bottom", bottom); + + if(mMovieHandle) + { + message.setValueReal("current_time", getCurrentTime()); + message.setValueReal("duration", getDuration()); + message.setValueReal("current_rate", Fix2X(GetMovieRate(mMovieHandle))); + } + + sendMessage(message); + } + + + static Rect rectFromSize(int width, int height) + { + Rect result; + + + result.left = 0; + result.top = 0; + result.right = width; + result.bottom = height; + + return result; + } + + Fixed getPlayRate(void) + { + Fixed result; + if(mPlayRate == 0.0f) + { + // Default to the movie's preferred rate + result = GetMoviePreferredRate(mMovieHandle); + if(result == 0) + { + // Don't return a 0 play rate, ever. + std::cerr << "Movie's preferred rate is 0, forcing to 1.0." << std::endl; + result = X2Fix(1.0f); + } + } + else + { + result = X2Fix(mPlayRate); + } + + return result; + } + + void load( const std::string url ) + { + if ( url.empty() ) + return; + + // Stop and unload any existing movie before starting another one. + unload(); + + setStatus(STATUS_LOADING); + + //In case std::string::c_str() makes a copy of the url data, + //make sure there is memory to hold it before allocating memory for handle. + //if fails, NewHandleClear(...) should return NULL. + const char* url_string = url.c_str() ; + Handle handle = NewHandleClear( ( Size )( url.length() + 1 ) ); + if ( NULL == handle || noErr != MemError() || NULL == *handle ) + { + setStatus(STATUS_ERROR); + return; + } + + BlockMove( url_string, *handle, ( Size )( url.length() + 1 ) ); + + OSErr err = NewMovieFromDataRef( &mMovieHandle, newMovieActive | newMovieDontInteractWithUser | newMovieAsyncOK | newMovieIdleImportOK, nil, handle, URLDataHandlerSubType ); + DisposeHandle( handle ); + if ( noErr != err ) + { + setStatus(STATUS_ERROR); + return; + }; + + // do pre-roll actions (typically fired for streaming movies but not always) + PrePrerollMovie( mMovieHandle, 0, getPlayRate(), moviePrePrerollCompleteCallback, ( void * )this ); + + Rect movie_rect = rectFromSize(mWidth, mHeight); + + // make a new movie controller + mMovieController = NewMovieController( mMovieHandle, &movie_rect, mcNotVisible | mcTopLeftMovie ); + + // movie controller + MCSetActionFilterWithRefCon( mMovieController, mcActionFilterCallBack, ( long )this ); + + SetMoviePlayHints( mMovieHandle, hintsAllowDynamicResize, hintsAllowDynamicResize ); + + // function that gets called when a frame is drawn + SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, movieDrawingCompleteCallback, ( long )this ); + + setStatus(STATUS_LOADED); + + sizeChanged(); + }; + + bool unload() + { + if ( mMovieHandle ) + { + StopMovie( mMovieHandle ); + if ( mMovieController ) + { + MCMovieChanged( mMovieController, mMovieHandle ); + }; + }; + + if ( mMovieController ) + { + MCSetActionFilterWithRefCon( mMovieController, NULL, (long)this ); + DisposeMovieController( mMovieController ); + mMovieController = NULL; + }; + + if ( mMovieHandle ) + { + SetMovieDrawingCompleteProc( mMovieHandle, movieDrawingCallWhenChanged, nil, ( long )this ); + DisposeMovie( mMovieHandle ); + mMovieHandle = NULL; + }; + + if ( mGWorldHandle ) + { + DisposeGWorld( mGWorldHandle ); + mGWorldHandle = NULL; + }; + + setStatus(STATUS_NONE); + + return true; + } + + bool navigateTo( const std::string url ) + { + unload(); + load( url ); + + return true; + }; + + bool sizeChanged() + { + if ( ! mMovieHandle ) + return false; + + // Check to see whether the movie's natural size has updated + { + int width, height; + getMovieNaturalSize(&width, &height); + if((width != 0) && (height != 0) && ((width != mNaturalWidth) || (height != mNaturalHeight))) + { + mNaturalWidth = width; + mNaturalHeight = height; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_request"); + message.setValue("name", mTextureSegmentName); + message.setValueS32("width", width); + message.setValueS32("height", height); + sendMessage(message); + //std::cerr << "<--- Sending size change request to application with name: " << mTextureSegmentName << " - size is " << width << " x " << height << std::endl; + } + } + + // sanitize destination size + Rect dest_rect = rectFromSize(mWidth, mHeight); + + // media depth won't change + int depth_bits = mDepth * 8; + long rowbytes = mDepth * mTextureWidth; + + GWorldPtr old_gworld_handle = mGWorldHandle; + + if(mPixels != NULL) + { + // We have pixels. Set up a GWorld pointing at the texture. + OSErr result = NewGWorldFromPtr( &mGWorldHandle, depth_bits, &dest_rect, NULL, NULL, 0, (Ptr)mPixels, rowbytes); + if ( noErr != result ) + { + // TODO: unrecoverable?? throw exception? return something? + return false; + } + } + else + { + // We don't have pixels. Create a fake GWorld we can point the movie at when it's not safe to render normally. + Rect tempRect = rectFromSize(1, 1); + OSErr result = NewGWorld( &mGWorldHandle, depth_bits, &tempRect, NULL, NULL, 0); + if ( noErr != result ) + { + // TODO: unrecoverable?? throw exception? return something? + return false; + } + } + + SetMovieGWorld( mMovieHandle, mGWorldHandle, GetGWorldDevice( mGWorldHandle ) ); + + // If the GWorld was already set up, delete it. + if(old_gworld_handle != NULL) + { + DisposeGWorld( old_gworld_handle ); + } + + // Set up the movie display matrix + { + // scale movie to fit rect and invert vertically to match opengl image format + MatrixRecord transform; + SetIdentityMatrix( &transform ); // transforms are additive so start from identify matrix + double scaleX = (double) mWidth / mNaturalWidth; + double scaleY = -1.0 * (double) mHeight / mNaturalHeight; + double centerX = mWidth / 2.0; + double centerY = mHeight / 2.0; + ScaleMatrix( &transform, X2Fix( scaleX ), X2Fix( scaleY ), X2Fix( centerX ), X2Fix( centerY ) ); + SetMovieMatrix( mMovieHandle, &transform ); + } + + // update movie controller + if ( mMovieController ) + { + MCSetControllerPort( mMovieController, mGWorldHandle ); + MCPositionController( mMovieController, &dest_rect, &dest_rect, + mcTopLeftMovie | mcPositionDontInvalidate ); + MCMovieChanged( mMovieController, mMovieHandle ); + } + + + // Emit event with size change so the calling app knows about it too + // TODO: + //LLMediaEvent event( this ); + //mEventEmitter.update( &LLMediaObserver::onMediaSizeChange, event ); + + return true; + } + + static Boolean mcActionFilterCallBack( MovieController mc, short action, void *params, long ref ) + { + Boolean result = false; + + MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; + + switch( action ) + { + // handle window resizing + case mcActionControllerSizeChanged: + // Ensure that the movie draws correctly at the new size + self->sizeChanged(); + break; + + // Block any movie controller actions that open URLs. + case mcActionLinkToURL: + case mcActionGetNextURL: + case mcActionLinkToURLExtended: + // Prevent the movie controller from handling the message + result = true; + break; + + default: + break; + }; + + return result; + }; + + static OSErr movieDrawingCompleteCallback( Movie call_back_movie, long ref ) + { + MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; + + // IMPORTANT: typically, a consumer who is observing this event will set a flag + // when this event is fired then render later. Be aware that the media stream + // can change during this period - dimensions, depth, format etc. + //LLMediaEvent event( self ); +// self->updateQuickTime(); + // TODO ^^^ + + if ( self->mWidth > 0 && self->mHeight > 0 ) + self->setDirty( 0, 0, self->mWidth, self->mHeight ); + + return noErr; + }; + + static void moviePrePrerollCompleteCallback( Movie movie, OSErr preroll_err, void *ref ) + { + //MediaPluginQuickTime* self = ( MediaPluginQuickTime* )ref; + + // TODO: + //LLMediaEvent event( self ); + //self->mEventEmitter.update( &LLMediaObserver::onMediaPreroll, event ); + }; + + + void rewind() + { + GoToBeginningOfMovie( mMovieHandle ); + MCMovieChanged( mMovieController, mMovieHandle ); + }; + + bool processState() + { + if ( mCommand == COMMAND_PLAY ) + { + if ( mStatus == STATUS_LOADED || mStatus == STATUS_PAUSED || mStatus == STATUS_PLAYING ) + { + long state = GetMovieLoadState( mMovieHandle ); + + if ( state >= kMovieLoadStatePlaythroughOK ) + { + // if the movie is at the end (generally because it reached it naturally) + // and we play is requested, jump back to the start of the movie. + // note: this is different from having loop flag set. + if ( IsMovieDone( mMovieHandle ) ) + { + Fixed rate = X2Fix( 0.0 ); + MCDoAction( mMovieController, mcActionPlay, (void*)rate ); + rewind(); + }; + + MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); + MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); + setStatus(STATUS_PLAYING); + mCommand = COMMAND_NONE; + }; + }; + } + else + if ( mCommand == COMMAND_STOP ) + { + if ( mStatus == STATUS_PLAYING || mStatus == STATUS_PAUSED ) + { + if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) + { + Fixed rate = X2Fix( 0.0 ); + MCDoAction( mMovieController, mcActionPlay, (void*)rate ); + rewind(); + + setStatus(STATUS_LOADED); + mCommand = COMMAND_NONE; + }; + }; + } + else + if ( mCommand == COMMAND_PAUSE ) + { + if ( mStatus == STATUS_PLAYING ) + { + if ( GetMovieLoadState( mMovieHandle ) >= kMovieLoadStatePlaythroughOK ) + { + Fixed rate = X2Fix( 0.0 ); + MCDoAction( mMovieController, mcActionPlay, (void*)rate ); + setStatus(STATUS_PAUSED); + mCommand = COMMAND_NONE; + }; + }; + }; + + return true; + }; + + void play(F64 rate) + { + mPlayRate = rate; + mCommand = COMMAND_PLAY; + }; + + void stop() + { + mCommand = COMMAND_STOP; + }; + + void pause() + { + mCommand = COMMAND_PAUSE; + }; + + void getMovieNaturalSize(int *movie_width, int *movie_height) + { + Rect rect; + + GetMovieNaturalBoundsRect( mMovieHandle, &rect ); + + int width = ( rect.right - rect.left ); + int height = ( rect.bottom - rect.top ); + + // make sure width and height fall in valid range + if ( width < mMinWidth ) + width = mMinWidth; + + if ( width > mMaxWidth ) + width = mMaxWidth; + + if ( height < mMinHeight ) + height = mMinHeight; + + if ( height > mMaxHeight ) + height = mMaxHeight; + + // return the new rect + *movie_width = width; + *movie_height = height; + } + + void updateQuickTime(int milliseconds) + { + if ( ! mMovieHandle ) + return; + + if ( ! mMovieController ) + return; + + // service QuickTime + // Calling it this way doesn't have good behavior on Windows... +// MoviesTask( mMovieHandle, milliseconds ); + // This was the original, but I think using both MoviesTask and MCIdle is redundant. Trying with only MCIdle. +// MoviesTask( mMovieHandle, 0 ); + + MCIdle( mMovieController ); + + if ( ! mGWorldHandle ) + return; + + if ( mMediaSizeChanging ) + return; + + // update state machine + processState(); + + // special code for looping - need to rewind at the end of the movie + if ( mIsLooping ) + { + // QT call to see if we are at the end - can't do with controller + if ( IsMovieDone( mMovieHandle ) ) + { + // go back to start + rewind(); + + if ( mMovieController ) + { + // kick off new play + MCDoAction( mMovieController, mcActionPrerollAndPlay, (void*)getPlayRate() ); + + // set the volume + MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); + }; + }; + }; + }; + + int getDataWidth() const + { + if ( mGWorldHandle ) + { + int depth = mDepth; + + if (depth < 1) + depth = 1; + + // ALWAYS use the row bytes from the PixMap if we have a GWorld because + // sometimes it's not the same as mMediaDepth * mMediaWidth ! + PixMapHandle pix_map_handle = GetGWorldPixMap( mGWorldHandle ); + return QTGetPixMapHandleRowBytes( pix_map_handle ) / depth; + } + else + { + // TODO : return LLMediaImplCommon::getaDataWidth(); + return 0; + } + }; + + void seek( F64 time ) + { + if ( mMovieController ) + { + TimeRecord when; + when.scale = GetMovieTimeScale( mMovieHandle ); + when.base = 0; + + // 'time' is in (floating point) seconds. The timebase time will be in 'units', where + // there are 'scale' units per second. + SInt64 raw_time = ( SInt64 )( time * (double)( when.scale ) ); + + when.value.hi = ( SInt32 )( raw_time >> 32 ); + when.value.lo = ( SInt32 )( ( raw_time & 0x00000000FFFFFFFF ) ); + + MCDoAction( mMovieController, mcActionGoToTime, &when ); + }; + }; + + F64 getDuration() + { + TimeValue duration = GetMovieDuration( mMovieHandle ); + TimeValue scale = GetMovieTimeScale( mMovieHandle ); + + return (F64)duration / (F64)scale; + }; + + F64 getCurrentTime() + { + TimeValue curr_time = GetMovieTime( mMovieHandle, 0 ); + TimeValue scale = GetMovieTimeScale( mMovieHandle ); + + return (F64)curr_time / (F64)scale; + }; + + void setVolume( F64 volume ) + { + mCurVolume = (short)(volume * ( double ) 0x100 ); + + if ( mMovieController ) + { + MCDoAction( mMovieController, mcActionSetVolume, (void*)mCurVolume ); + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void update(int milliseconds = 0) + { + updateQuickTime(milliseconds); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseDown( int x, int y ) + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseUp( int x, int y ) + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseMove( int x, int y ) + { + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void keyPress( unsigned char key ) + { + }; + +}; + +MediaPluginQuickTime::MediaPluginQuickTime( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) : + MediaPluginBase(host_send_func, host_user_data), + mMinWidth( 0 ), + mMaxWidth( 2048 ), + mMinHeight( 0 ), + mMaxHeight( 2048 ) +{ +// std::cerr << "MediaPluginQuickTime constructor" << std::endl; + + mNaturalWidth = -1; + mNaturalHeight = -1; + mMovieHandle = 0; + mGWorldHandle = 0; + mMovieController = 0; + mCurVolume = 0x99; + mMediaSizeChanging = false; + mIsLooping = false; + mCommand = COMMAND_NONE; + mPlayRate = 0.0f; + mStatus = STATUS_NONE; +} + +MediaPluginQuickTime::~MediaPluginQuickTime() +{ +// std::cerr << "MediaPluginQuickTime destructor" << std::endl; + + ExitMovies(); + +#ifdef LL_WINDOWS + TerminateQTML(); +// std::cerr << "QuickTime closing down" << std::endl; +#endif +} + + +void MediaPluginQuickTime::receiveMessage(const char *message_string) +{ +// std::cerr << "MediaPluginQuickTime::receiveMessage: received message: \"" << message_string << "\"" << std::endl; + LLPluginMessage message_in; + + if(message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if(message_name == "init") + { + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + // Normally a plugin would only specify one of these two subclasses, but this is a demo... +// versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME] = LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME_VERSION; + message.setValueLLSD("versions", versions); + + #ifdef LL_WINDOWS + if ( InitializeQTML( 0L ) != noErr ) + { + //TODO: If no QT on Windows, this fails - respond accordingly. + //return false; + } + else + { +// std::cerr << "QuickTime initialized" << std::endl; + }; + #endif + + EnterMovies(); + + std::string plugin_version = "QuickTime media plugin, QuickTime version "; + + long version = 0; + Gestalt( gestaltQuickTimeVersion, &version ); + std::ostringstream codec( "" ); + codec << std::hex << version << std::dec; + plugin_version += codec.str(); + message.setValue("plugin_version", plugin_version); + sendMessage(message); + + // Plugin gets to decide the texture parameters to use. + message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + #if defined(LL_WINDOWS) + // Values for Windows + mDepth = 3; + message.setValueU32("format", GL_RGB); + message.setValueU32("type", GL_UNSIGNED_BYTE); + + // We really want to pad the texture width to a multiple of 32 bytes, but since we're using 3-byte pixels, it doesn't come out even. + // Padding to a multiple of 3*32 guarantees it'll divide out properly. + message.setValueU32("padding", 32 * 3); + #else + // Values for Mac + mDepth = 4; + message.setValueU32("format", GL_BGRA_EXT); + #ifdef __BIG_ENDIAN__ + message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8_REV ); + #else + message.setValueU32("type", GL_UNSIGNED_INT_8_8_8_8); + #endif + + // Pad texture width to a multiple of 32 bytes, to line up with cache lines. + message.setValueU32("padding", 32); + #endif + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGB); + message.setValueBoolean("coords_opengl", true); // true == use OpenGL-style coordinates, false == (0,0) is upper left. + message.setValueBoolean("allow_downsample", true); + sendMessage(message); + } + else if(message_name == "idle") + { + // no response is necessary here. + F64 time = message_in.getValueReal("time"); + + // Convert time to milliseconds for update() + update((int)(time * 1000.0f)); + } + else if(message_name == "cleanup") + { + // TODO: clean up here + } + else if(message_name == "shm_added") + { + SharedSegmentInfo info; + U64 address_lo = message_in.getValueU32("address"); + U64 address_hi = message_in.hasValue("address_1") ? message_in.getValueU32("address_1") : 0; + info.mAddress = (void*)((address_lo) | + (address_hi * (U64(1)<<31))); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); + + +// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory added, name: " << name +// << ", size: " << info.mSize +// << ", address: " << info.mAddress +// << std::endl; + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if(message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + +// std::cerr << "MediaPluginQuickTime::receiveMessage: shared memory remove, name = " << name << std::endl; + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + if(mPixels == iter->second.mAddress) + { + // This is the currently active pixel buffer. Make sure we stop drawing to it. + mPixels = NULL; + mTextureSegmentName.clear(); + + // Make sure the movie GWorld is no longer pointed at the shared segment. + sizeChanged(); + } + mSharedSegments.erase(iter); + } + else + { +// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown shared memory region!" << std::endl; + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { +// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if(message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + //std::cerr << "---->Got size change instruction from application with name: " << name << " - size is " << width << " x " << height << std::endl; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + + if(!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { +// std::cerr << "%%% Got size change, new size is " << width << " by " << height << std::endl; +// std::cerr << "%%%% texture size is " << texture_width << " by " << texture_height << std::endl; + + mPixels = (unsigned char*)iter->second.mAddress; + mTextureSegmentName = name; + mWidth = width; + mHeight = height; + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + mMediaSizeChanging = false; + + sizeChanged(); + + update(); + }; + }; + } + else if(message_name == "load_uri") + { + std::string uri = message_in.getValue("uri"); + load( uri ); + sendStatus(); + } + else if(message_name == "mouse_event") + { + std::string event = message_in.getValue("event"); + S32 x = message_in.getValueS32("x"); + S32 y = message_in.getValueS32("y"); + + if(event == "down") + { + mouseDown(x, y); + } + else if(event == "up") + { + mouseUp(x, y); + } + else if(event == "move") + { + mouseMove(x, y); + }; + }; + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_TIME) + { + if(message_name == "stop") + { + stop(); + } + else if(message_name == "start") + { + F64 rate = 0.0; + if(message_in.hasValue("rate")) + { + rate = message_in.getValueReal("rate"); + } + play(rate); + } + else if(message_name == "pause") + { + pause(); + } + else if(message_name == "seek") + { + F64 time = message_in.getValueReal("time"); + seek(time); + } + else if(message_name == "set_loop") + { + bool loop = message_in.getValueBoolean("loop"); + mIsLooping = loop; + } + else if(message_name == "set_volume") + { + F64 volume = message_in.getValueReal("volume"); + setVolume(volume); + } + } + else + { +// std::cerr << "MediaPluginQuickTime::receiveMessage: unknown message class: " << message_class << std::endl; + }; + }; +} + +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + MediaPluginQuickTime *self = new MediaPluginQuickTime(host_send_func, host_user_data); + *plugin_send_func = MediaPluginQuickTime::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} + +#else // LL_QUICKTIME_ENABLED + +// Stubbed-out class with constructor/destructor (necessary or windows linker +// will just think its dead code and optimize it all out) +class MediaPluginQuickTime : public MediaPluginBase +{ +public: + MediaPluginQuickTime(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginQuickTime(); + /* virtual */ void receiveMessage(const char *message_string); +}; + +MediaPluginQuickTime::MediaPluginQuickTime( + LLPluginInstance::sendMessageFunction host_send_func, + void *host_user_data ) : + MediaPluginBase(host_send_func, host_user_data) +{ + // no-op +} + +MediaPluginQuickTime::~MediaPluginQuickTime() +{ + // no-op +} + +void MediaPluginQuickTime::receiveMessage(const char *message_string) +{ + // no-op +} + +// We're building without quicktime enabled. Just refuse to initialize. +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + return -1; +} + +#endif // LL_QUICKTIME_ENABLED diff --git a/indra/media_plugins/webkit/CMakeLists.txt b/indra/media_plugins/webkit/CMakeLists.txt new file mode 100644 index 0000000000..c048dd66c1 --- /dev/null +++ b/indra/media_plugins/webkit/CMakeLists.txt @@ -0,0 +1,74 @@ +# -*- cmake -*- + +project(media_plugin_webkit) + +include(00-Common) +include(LLCommon) +include(LLImage) +include(LLPlugin) +include(LLMath) +include(LLRender) +include(LLWindow) +include(Linking) +include(PluginAPI) +include(MediaPluginBase) +include(FindOpenGL) + +include(WebKitLibPlugin) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${MEDIA_PLUGIN_BASE_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} +) + + +### media_plugin_webkit + +set(media_plugin_webkit_SOURCE_FILES + media_plugin_webkit.cpp + ) + +add_library(media_plugin_webkit + SHARED + ${media_plugin_webkit_SOURCE_FILES} +) + +target_link_libraries(media_plugin_webkit + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${WEBKIT_PLUGIN_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +add_dependencies(media_plugin_webkit + ${LLPLUGIN_LIBRARIES} + ${MEDIA_PLUGIN_BASE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + +if (DARWIN) + # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name + set_target_properties( + media_plugin_webkit + PROPERTIES + PREFIX "" + BUILD_WITH_INSTALL_RPATH 1 + INSTALL_NAME_DIR "@executable_path" + LINK_FLAGS "-exported_symbols_list ${CMAKE_CURRENT_SOURCE_DIR}/../base/media_plugin_base.exp" + ) + + # copy the webkit dylib to the build directory + add_custom_command( + TARGET media_plugin_webkit POST_BUILD +# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/libllwebkitlib.dylib + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllwebkitlib.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS media_plugin_webkit ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllwebkitlib.dylib + ) + +endif (DARWIN)
\ No newline at end of file diff --git a/indra/media_plugins/webkit/media_plugin_webkit.cpp b/indra/media_plugins/webkit/media_plugin_webkit.cpp new file mode 100644 index 0000000000..bd29eb5395 --- /dev/null +++ b/indra/media_plugins/webkit/media_plugin_webkit.cpp @@ -0,0 +1,787 @@ +/** + * @file media_plugin_webkit.cpp + * @brief Webkit plugin for LLMedia API plugin system + * + * $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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llwebkitlib.h" + +#include "linden_common.h" +#include "indra_constants.h" // for indra keyboard codes + +#include "llgl.h" + +#include "llplugininstance.h" +#include "llpluginmessage.h" +#include "llpluginmessageclasses.h" +#include "media_plugin_base.h" + +#if LL_WINDOWS +#include <direct.h> +#else +#include <unistd.h> +#include <stdlib.h> +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +class MediaPluginWebKit : + public MediaPluginBase, + public LLEmbeddedBrowserWindowObserver +{ +public: + MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data); + ~MediaPluginWebKit(); + + /*virtual*/ void receiveMessage(const char *message_string); + +private: + + int mBrowserWindowId; + bool mBrowserInitialized; + bool mNeedsUpdate; + + bool mCanCut; + bool mCanCopy; + bool mCanPaste; + + //////////////////////////////////////////////////////////////////////////////// + // + void update(int milliseconds) + { + LLMozLib::getInstance()->pump( milliseconds ); + + checkEditState(); + + if ( mNeedsUpdate ) + { + const unsigned char* browser_pixels = LLMozLib::getInstance()->grabBrowserWindow( mBrowserWindowId ); + + unsigned int buffer_size = LLMozLib::getInstance()->getBrowserRowSpan( mBrowserWindowId ) * LLMozLib::getInstance()->getBrowserHeight( mBrowserWindowId ); + +// std::cerr << "webkit plugin: updating" << std::endl; + + // TODO: should get rid of this memcpy if possible + if ( mPixels && browser_pixels ) + { +// std::cerr << " memcopy of " << buffer_size << " bytes" << std::endl; + memcpy( mPixels, browser_pixels, buffer_size ); + } + + if ( mWidth > 0 && mHeight > 0 ) + { +// std::cerr << "Setting dirty, " << mWidth << " x " << mHeight << std::endl; + setDirty( 0, 0, mWidth, mHeight ); + } + + mNeedsUpdate = false; + }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + bool initBrowser() + { + // already initialized + if ( mBrowserInitialized ) + return true; + + // not enough information to initialize the browser yet. + if ( mWidth < 0 || mHeight < 0 || mDepth < 0 || + mTextureWidth < 0 || mTextureHeight < 0 ) + { + return false; + }; + + // set up directories + char cwd[ FILENAME_MAX ]; // I *think* this is defined on all platforms we use + if (NULL == getcwd( cwd, FILENAME_MAX - 1 )) + { + llwarns << "Couldn't get cwd - probably too long - failing to init." << llendl; + return false; + } + std::string application_dir = std::string( cwd ); + std::string component_dir = application_dir; + std::string profileDir = application_dir + "/" + "browser_profile"; // cross platform? + + // window handle - needed on Windows and must be app window. +#if LL_WINDOWS + char window_title[ MAX_PATH ]; + GetConsoleTitleA( window_title, MAX_PATH ); + void* native_window_handle = (void*)FindWindowA( NULL, window_title ); +#else + void* native_window_handle = 0; +#endif + + // main browser initialization + bool result = LLMozLib::getInstance()->init( application_dir, component_dir, profileDir, native_window_handle ); + if ( result ) + { + // create single browser window + mBrowserWindowId = LLMozLib::getInstance()->createBrowserWindow( mWidth, mHeight ); + + // Enable plugins + LLMozLib::getInstance()->enablePlugins(true); + + // tell LLMozLib about the size of the browser window + LLMozLib::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); + + // observer events that LLMozLib emits + LLMozLib::getInstance()->addObserver( mBrowserWindowId, this ); + + // append details to agent string + LLMozLib::getInstance()->setBrowserAgentId( "LLPluginMedia Web Browser" ); + + // don't flip bitmap + LLMozLib::getInstance()->flipWindow( mBrowserWindowId, true ); + + // go to the "home page" + // Don't do this here -- it causes the dreaded "white flash" when loading a browser instance. +// LLMozLib::getInstance()->navigateTo( mBrowserWindowId, "about:blank" ); + + // set flag so we don't do this again + mBrowserInitialized = true; + + return true; + }; + + return false; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onCursorChanged(const EventType& event) + { + LLMozLib::ECursor moz_cursor = (LLMozLib::ECursor)event.getIntValue(); + std::string name; + + switch(moz_cursor) + { + case LLMozLib::C_ARROW: + name = "arrow"; + break; + case LLMozLib::C_IBEAM: + name = "ibeam"; + break; + case LLMozLib::C_SPLITV: + name = "splitv"; + break; + case LLMozLib::C_SPLITH: + name = "splith"; + break; + case LLMozLib::C_POINTINGHAND: + name = "hand"; + break; + + default: + llwarns << "Unknown cursor ID: " << (int)moz_cursor << llendl; + break; + } + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "cursor_changed"); + message.setValue("name", name); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onPageChanged( const EventType& event ) + { + // flag that an update is required + mNeedsUpdate = true; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onNavigateBegin(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_begin"); + message.setValue("uri", event.getEventUri()); + sendMessage(message); + + setStatus(STATUS_LOADING); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onNavigateComplete(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "navigate_complete"); + message.setValue("uri", event.getEventUri()); + message.setValueS32("result_code", event.getIntValue()); + message.setValue("result_string", event.getStringValue()); + message.setValueBoolean("history_back_available", LLMozLib::getInstance()->userActionIsEnabled( mBrowserWindowId, LLMozLib::UA_NAVIGATE_BACK)); + message.setValueBoolean("history_forward_available", LLMozLib::getInstance()->userActionIsEnabled( mBrowserWindowId, LLMozLib::UA_NAVIGATE_FORWARD)); + sendMessage(message); + + setStatus(STATUS_LOADED); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onUpdateProgress(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "progress"); + message.setValueS32("percent", event.getIntValue()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onStatusTextChange(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "status_text"); + message.setValue("status", event.getStringValue()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onLocationChange(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "location_changed"); + message.setValue("uri", event.getEventUri()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onClickLinkHref(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_href"); + message.setValue("uri", event.getStringValue()); + message.setValue("target", event.getStringValue2()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onClickLinkNoFollow(const EventType& event) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER, "click_nofollow"); + message.setValue("uri", event.getStringValue()); + sendMessage(message); + } + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseDown( int x, int y ) + { + LLMozLib::getInstance()->mouseDown( mBrowserWindowId, x, y ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseUp( int x, int y ) + { + LLMozLib::getInstance()->mouseUp( mBrowserWindowId, x, y ); + LLMozLib::getInstance()->focusBrowser( mBrowserWindowId, true ); + checkEditState(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseMove( int x, int y ) + { + LLMozLib::getInstance()->mouseMove( mBrowserWindowId, x, y ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void keyPress( int key ) + { + int moz_key; + + // The incoming values for 'key' will be the ones from indra_constants.h + // the outgoing values are the ones from llwebkitlib.h + + switch((KEY)key) + { + // This is the list that the qtwebkit-llmozlib implementation actually maps into Qt keys. +// case KEY_XXX: moz_key = LL_DOM_VK_CANCEL; break; +// case KEY_XXX: moz_key = LL_DOM_VK_HELP; break; + case KEY_BACKSPACE: moz_key = LL_DOM_VK_BACK_SPACE; break; + case KEY_TAB: moz_key = LL_DOM_VK_TAB; break; +// case KEY_XXX: moz_key = LL_DOM_VK_CLEAR; break; + case KEY_RETURN: moz_key = LL_DOM_VK_RETURN; break; + case KEY_PAD_RETURN: moz_key = LL_DOM_VK_ENTER; break; + case KEY_SHIFT: moz_key = LL_DOM_VK_SHIFT; break; + case KEY_CONTROL: moz_key = LL_DOM_VK_CONTROL; break; + case KEY_ALT: moz_key = LL_DOM_VK_ALT; break; +// case KEY_XXX: moz_key = LL_DOM_VK_PAUSE; break; + case KEY_CAPSLOCK: moz_key = LL_DOM_VK_CAPS_LOCK; break; + case KEY_ESCAPE: moz_key = LL_DOM_VK_ESCAPE; break; + case KEY_PAGE_UP: moz_key = LL_DOM_VK_PAGE_UP; break; + case KEY_PAGE_DOWN: moz_key = LL_DOM_VK_PAGE_DOWN; break; + case KEY_END: moz_key = LL_DOM_VK_END; break; + case KEY_HOME: moz_key = LL_DOM_VK_HOME; break; + case KEY_LEFT: moz_key = LL_DOM_VK_LEFT; break; + case KEY_UP: moz_key = LL_DOM_VK_UP; break; + case KEY_RIGHT: moz_key = LL_DOM_VK_RIGHT; break; + case KEY_DOWN: moz_key = LL_DOM_VK_DOWN; break; +// case KEY_XXX: moz_key = LL_DOM_VK_PRINTSCREEN; break; + case KEY_INSERT: moz_key = LL_DOM_VK_INSERT; break; + case KEY_DELETE: moz_key = LL_DOM_VK_DELETE; break; +// case KEY_XXX: moz_key = LL_DOM_VK_CONTEXT_MENU; break; + + default: + if(key < KEY_SPECIAL) + { + // Pass the incoming key through -- it should be regular ASCII, which should be correct for webkit. + moz_key = key; + } + else + { + // Don't pass through untranslated special keys -- they'll be all wrong. + moz_key = 0; + } + break; + } + +// std::cerr << "keypress, original code = 0x" << std::hex << key << ", converted code = 0x" << std::hex << moz_key << std::dec << std::endl; + + if(moz_key != 0) + { + LLMozLib::getInstance()->keyPress( mBrowserWindowId, moz_key ); + } + + checkEditState(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void unicodeInput( const std::string &utf8str ) + { + LLWString wstr = utf8str_to_wstring(utf8str); + + unsigned int i; + for(i=0; i < wstr.size(); i++) + { +// std::cerr << "unicode input, code = 0x" << std::hex << (unsigned long)(wstr[i]) << std::dec << std::endl; + + LLMozLib::getInstance()->unicodeInput(mBrowserWindowId, wstr[i]); + } + + checkEditState(); + }; + + void checkEditState(void) + { + bool can_cut = LLMozLib::getInstance()->userActionIsEnabled( mBrowserWindowId, LLMozLib::UA_EDIT_CUT); + bool can_copy = LLMozLib::getInstance()->userActionIsEnabled( mBrowserWindowId, LLMozLib::UA_EDIT_COPY); + bool can_paste = LLMozLib::getInstance()->userActionIsEnabled( mBrowserWindowId, LLMozLib::UA_EDIT_PASTE); + + if((can_cut != mCanCut) || (can_copy != mCanCopy) || (can_paste != mCanPaste)) + { + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "edit_state"); + + if(can_cut != mCanCut) + { + mCanCut = can_cut; + message.setValueBoolean("cut", can_cut); + } + + if(can_copy != mCanCopy) + { + mCanCopy = can_copy; + message.setValueBoolean("copy", can_copy); + } + + if(can_paste != mCanPaste) + { + mCanPaste = can_paste; + message.setValueBoolean("paste", can_paste); + } + + sendMessage(message); + + } + } + +}; + +MediaPluginWebKit::MediaPluginWebKit(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data) : + MediaPluginBase(host_send_func, host_user_data) +{ +// std::cerr << "MediaPluginWebKit constructor" << std::endl; + + mBrowserWindowId = 0; + mBrowserInitialized = false; + mNeedsUpdate = true; + mCanCut = false; + mCanCopy = false; + mCanPaste = false; +} + +MediaPluginWebKit::~MediaPluginWebKit() +{ + // unhook observer + LLMozLib::getInstance()->remObserver( mBrowserWindowId, this ); + + // clean up + LLMozLib::getInstance()->reset(); + +// std::cerr << "MediaPluginWebKit destructor" << std::endl; +} + +void MediaPluginWebKit::receiveMessage(const char *message_string) +{ +// std::cerr << "MediaPluginWebKit::receiveMessage: received message: \"" << message_string << "\"" << std::endl; + LLPluginMessage message_in; + + if(message_in.parse(message_string) >= 0) + { + std::string message_class = message_in.getClass(); + std::string message_name = message_in.getName(); + if(message_class == LLPLUGIN_MESSAGE_CLASS_BASE) + { + if(message_name == "init") + { + LLPluginMessage message("base", "init_response"); + LLSD versions = LLSD::emptyMap(); + versions[LLPLUGIN_MESSAGE_CLASS_BASE] = LLPLUGIN_MESSAGE_CLASS_BASE_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA] = LLPLUGIN_MESSAGE_CLASS_MEDIA_VERSION; + versions[LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER] = LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER_VERSION; + message.setValueLLSD("versions", versions); + + std::string plugin_version = "Webkit media plugin, Webkit version "; + plugin_version += LLMozLib::getInstance()->getVersion(); + message.setValue("plugin_version", plugin_version); + sendMessage(message); + + // Plugin gets to decide the texture parameters to use. + mDepth = 4; + + message.setMessage(LLPLUGIN_MESSAGE_CLASS_MEDIA, "texture_params"); + message.setValueS32("default_width", 800); + message.setValueS32("default_height", 600); + message.setValueS32("depth", mDepth); + message.setValueU32("internalformat", GL_RGBA); + message.setValueU32("format", GL_RGBA); + message.setValueU32("type", GL_UNSIGNED_BYTE); + message.setValueBoolean("coords_opengl", true); + sendMessage(message); + } + else if(message_name == "idle") + { + // no response is necessary here. + F64 time = message_in.getValueReal("time"); + + // Convert time to milliseconds for update() + update((int)(time * 1000.0f)); + } + else if(message_name == "cleanup") + { + // TODO: clean up here + } + else if(message_name == "shm_added") + { + SharedSegmentInfo info; + U64 address_lo = message_in.getValueU32("address"); + U64 address_hi = message_in.hasValue("address_1") ? message_in.getValueU32("address_1") : 0; + info.mAddress = (void*)((address_lo) | + (address_hi * (U64(1)<<31))); + info.mSize = (size_t)message_in.getValueS32("size"); + std::string name = message_in.getValue("name"); + + +// std::cerr << "MediaPluginWebKit::receiveMessage: shared memory added, name: " << name +// << ", size: " << info.mSize +// << ", address: " << info.mAddress +// << std::endl; + + mSharedSegments.insert(SharedSegmentMap::value_type(name, info)); + + } + else if(message_name == "shm_remove") + { + std::string name = message_in.getValue("name"); + +// std::cerr << "MediaPluginWebKit::receiveMessage: shared memory remove, name = " << name << std::endl; + + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + if(mPixels == iter->second.mAddress) + { + // This is the currently active pixel buffer. Make sure we stop drawing to it. + mPixels = NULL; + mTextureSegmentName.clear(); + } + mSharedSegments.erase(iter); + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown shared memory region!" << std::endl; + } + + // Send the response so it can be cleaned up. + LLPluginMessage message("base", "shm_remove_response"); + message.setValue("name", name); + sendMessage(message); + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown base message: " << message_name << std::endl; + } + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA) + { + if(message_name == "size_change") + { + std::string name = message_in.getValue("name"); + S32 width = message_in.getValueS32("width"); + S32 height = message_in.getValueS32("height"); + S32 texture_width = message_in.getValueS32("texture_width"); + S32 texture_height = message_in.getValueS32("texture_height"); + + if(!name.empty()) + { + // Find the shared memory region with this name + SharedSegmentMap::iterator iter = mSharedSegments.find(name); + if(iter != mSharedSegments.end()) + { + mPixels = (unsigned char*)iter->second.mAddress; + mWidth = width; + mHeight = height; + + // initialize (only gets called once) + initBrowser(); + + // size changed so tell the browser + LLMozLib::getInstance()->setSize( mBrowserWindowId, mWidth, mHeight ); + +// std::cerr << "webkit plugin: set size to " << mWidth << " x " << mHeight +// << ", rowspan is " << LLMozLib::getInstance()->getBrowserRowSpan(mBrowserWindowId) << std::endl; + + S32 real_width = LLMozLib::getInstance()->getBrowserRowSpan(mBrowserWindowId) / LLMozLib::getInstance()->getBrowserDepth(mBrowserWindowId); + + // The actual width the browser will be drawing to is probably smaller... let the host know by modifying texture_width in the response. + if(real_width <= texture_width) + { + texture_width = real_width; + } + else + { + // This won't work -- it'll be bigger than the allocated memory. This is a fatal error. +// std::cerr << "Fatal error: browser rowbytes greater than texture width" << std::endl; + mDeleteMe = true; + return; + } + + mTextureWidth = texture_width; + mTextureHeight = texture_height; + + }; + }; + + LLPluginMessage message(LLPLUGIN_MESSAGE_CLASS_MEDIA, "size_change_response"); + message.setValue("name", name); + message.setValueS32("width", width); + message.setValueS32("height", height); + message.setValueS32("texture_width", texture_width); + message.setValueS32("texture_height", texture_height); + sendMessage(message); + + } + else if(message_name == "load_uri") + { + std::string uri = message_in.getValue("uri"); + +// std::cout << "loading URI: " << uri << std::endl; + + if(!uri.empty()) + { + LLMozLib::getInstance()->navigateTo( mBrowserWindowId, uri ); + } + } + else if(message_name == "mouse_event") + { + std::string event = message_in.getValue("event"); + S32 x = message_in.getValueS32("x"); + S32 y = message_in.getValueS32("y"); + // std::string modifiers = message.getValue("modifiers"); + + if(event == "down") + { + mouseDown(x, y); + //std::cout << "Mouse down at " << x << " x " << y << std::endl; + } + else if(event == "up") + { + mouseUp(x, y); + //std::cout << "Mouse up at " << x << " x " << y << std::endl; + } + else if(event == "move") + { + mouseMove(x, y); + //std::cout << ">>>>>>>>>>>>>>>>>>>> Mouse move at " << x << " x " << y << std::endl; + } + } + else if(message_name == "scroll_event") + { + // S32 x = message_in.getValueS32("x"); + S32 y = message_in.getValueS32("y"); + // std::string modifiers = message.getValue("modifiers"); + + // We currently ignore horizontal scrolling. + // The scroll values are roughly 1 per wheel click, so we need to magnify them by some factor. + // Arbitrarily, I choose 16. + y *= 16; + LLMozLib::getInstance()->scrollByLines(mBrowserWindowId, y); + } + else if(message_name == "key_event") + { + std::string event = message_in.getValue("event"); + + // act on "key down" or "key repeat" + if ( (event == "down") || (event == "repeat") ) + { + S32 key = message_in.getValueS32("key"); + keyPress( key ); + }; + } + else if(message_name == "text_event") + { + std::string text = message_in.getValue("text"); + + unicodeInput(text); + } + if(message_name == "edit_cut") + { + LLMozLib::getInstance()->userAction( mBrowserWindowId, LLMozLib::UA_EDIT_CUT ); + } + if(message_name == "edit_copy") + { + LLMozLib::getInstance()->userAction( mBrowserWindowId, LLMozLib::UA_EDIT_COPY ); + } + if(message_name == "edit_paste") + { + LLMozLib::getInstance()->userAction( mBrowserWindowId, LLMozLib::UA_EDIT_PASTE ); + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media message: " << message_string << std::endl; + }; + } + else if(message_class == LLPLUGIN_MESSAGE_CLASS_MEDIA_BROWSER) + { + if(message_name == "focus") + { + bool val = message_in.getValueBoolean("focused"); + LLMozLib::getInstance()->focusBrowser( mBrowserWindowId, val ); + } + else if(message_name == "clear_cache") + { + LLMozLib::getInstance()->clearCache(); + } + else if(message_name == "clear_cookies") + { + LLMozLib::getInstance()->clearAllCookies(); + } + else if(message_name == "enable_cookies") + { + bool val = message_in.getValueBoolean("enable"); + LLMozLib::getInstance()->enableCookies( val ); + } + else if(message_name == "proxy_setup") + { + bool val = message_in.getValueBoolean("enable"); + std::string host = message_in.getValue("host"); + int port = message_in.getValueS32("port"); + LLMozLib::getInstance()->enableProxy( val, host, port ); + } + else if(message_name == "browse_stop") + { + LLMozLib::getInstance()->userAction( mBrowserWindowId, LLMozLib::UA_NAVIGATE_STOP ); + } + else if(message_name == "browse_reload") + { + // foo = message_in.getValueBoolean("ignore_cache"); + LLMozLib::getInstance()->userAction( mBrowserWindowId, LLMozLib::UA_NAVIGATE_RELOAD ); + } + else if(message_name == "browse_forward") + { + LLMozLib::getInstance()->userAction( mBrowserWindowId, LLMozLib::UA_NAVIGATE_FORWARD ); + } + else if(message_name == "browse_back") + { + LLMozLib::getInstance()->userAction( mBrowserWindowId, LLMozLib::UA_NAVIGATE_BACK ); + } + else if(message_name == "set_status_redirect") + { + int code = message_in.getValueS32("code"); + std::string url = message_in.getValue("url"); + if ( 404 == code ) // browser lib only supports 404 right now + { + LLMozLib::getInstance()->set404RedirectUrl( mBrowserWindowId, url ); + }; + } + else if(message_name == "set_user_agent") + { + std::string user_agent = message_in.getValue("user_agent"); + LLMozLib::getInstance()->setBrowserAgentId( user_agent ); + } + else if(message_name == "init_history") + { + // Initialize browser history + LLSD history = message_in.getValueLLSD("history"); + // First, clear the URL history + LLMozLib::getInstance()->clearHistory(mBrowserWindowId); + // Then, add the history items in order + LLSD::array_iterator iter_history = history.beginArray(); + LLSD::array_iterator end_history = history.endArray(); + for(; iter_history != end_history; ++iter_history) + { + std::string url = (*iter_history).asString(); + if(! url.empty()) { + LLMozLib::getInstance()->prependHistoryUrl(mBrowserWindowId, url); + } + } + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown media_browser message: " << message_string << std::endl; + }; + } + else + { +// std::cerr << "MediaPluginWebKit::receiveMessage: unknown message class: " << message_class << std::endl; + }; + } +} + +int init_media_plugin(LLPluginInstance::sendMessageFunction host_send_func, void *host_user_data, LLPluginInstance::sendMessageFunction *plugin_send_func, void **plugin_user_data) +{ + MediaPluginWebKit *self = new MediaPluginWebKit(host_send_func, host_user_data); + *plugin_send_func = MediaPluginWebKit::staticReceiveMessage; + *plugin_user_data = (void*)self; + + return 0; +} + diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 37df00d9ec..d7bb68a006 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -18,17 +18,17 @@ include(LLImage) include(LLImageJ2COJ) include(LLInventory) include(LLMath) -include(LLMedia) include(LLMessage) +include(LLPlugin) include(LLPrimitive) include(LLRender) include(LLUI) include(LLVFS) include(LLWindow) include(LLXML) +include(LLXUIXML) include(LScript) include(Linking) -include(Mozlib) include(NDOF) include(GooglePerfTools) include(TemplateCheck) @@ -47,14 +47,15 @@ include_directories( ${LLIMAGE_INCLUDE_DIRS} ${LLINVENTORY_INCLUDE_DIRS} ${LLMATH_INCLUDE_DIRS} - ${LLMEDIA_INCLUDE_DIRS} ${LLMESSAGE_INCLUDE_DIRS} + ${LLPLUGIN_INCLUDE_DIRS} ${LLPRIMITIVE_INCLUDE_DIRS} ${LLRENDER_INCLUDE_DIRS} ${LLUI_INCLUDE_DIRS} ${LLVFS_INCLUDE_DIRS} ${LLWINDOW_INCLUDE_DIRS} ${LLXML_INCLUDE_DIRS} + ${LLXUIXML_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS} ${LSCRIPT_INCLUDE_DIRS}/lscript_compile ${LLLOGIN_INCLUDE_DIRS} @@ -63,12 +64,14 @@ include_directories( set(viewer_SOURCE_FILES llaccordionctrltab.cpp llaccordionctrl.cpp + llactiveimwindow.cpp llagent.cpp llagentlistener.cpp llagentaccess.cpp llagentdata.cpp llagentlanguage.cpp llagentpilot.cpp + llagentui.cpp llagentwearables.cpp llanimstatelabels.cpp llappviewer.cpp @@ -165,9 +168,8 @@ set(viewer_SOURCE_FILES llfloatergroups.cpp llfloaterhandler.cpp llfloaterhardwaresettings.cpp - llfloaterhtml.cpp llfloaterhtmlcurrency.cpp - llfloaterhtmlhelp.cpp + llfloatermediabrowser.cpp llfloaterhtmlsimple.cpp llfloaterhud.cpp llfloaterimagepreview.cpp @@ -232,6 +234,7 @@ set(viewer_SOURCE_FILES llhudrender.cpp llhudtext.cpp llhudview.cpp + llimhandler.cpp llimpanel.cpp llimview.cpp llimcontrolpanel.cpp @@ -305,12 +308,14 @@ set(viewer_SOURCE_FILES llpanelinventory.cpp llpanelimcontrolpanel.cpp llpanelland.cpp + llpanellandaudio.cpp llpanellandmarks.cpp llpanellandmedia.cpp llpanellogin.cpp llpanellookinfo.cpp llpanellooks.cpp llpanelmedia.cpp + llpanelmediahud.cpp llpanelmeprofile.cpp llpanelmovetip.cpp llpanelobject.cpp @@ -361,6 +366,7 @@ set(viewer_SOURCE_FILES llsyswellitem.cpp llsyswellwindow.cpp llteleporthistory.cpp + llteleporthistorystorage.cpp lltexglobalcolor.cpp lltexlayer.cpp lltexlayerparams.cpp @@ -371,6 +377,7 @@ set(viewer_SOURCE_FILES lltoast.cpp lltoastalertpanel.cpp lltoastgroupnotifypanel.cpp + lltoastimpanel.cpp lltoastnotifypanel.cpp lltoastpanel.cpp lltoggleablemenu.cpp @@ -423,6 +430,8 @@ set(viewer_SOURCE_FILES llviewerkeyboard.cpp llviewerlayer.cpp llviewermedia.cpp + llviewermediafocus.cpp + llviewermedia_streamingaudio.cpp llviewermenu.cpp llviewermenufile.cpp llviewermessage.cpp @@ -475,7 +484,7 @@ set(viewer_SOURCE_FILES llwearabledictionary.cpp llwearablelist.cpp llweb.cpp - llwebbrowserctrl.cpp + llmediactrl.cpp llwind.cpp llwlanimator.cpp llwldaycycle.cpp @@ -512,12 +521,14 @@ set(viewer_HEADER_FILES ViewerInstall.cmake llaccordionctrltab.h llaccordionctrl.h + llactiveimwindow.h llagent.h llagentlistener.h llagentaccess.h llagentdata.h llagentlanguage.h llagentpilot.h + llagentui.h llagentwearables.h llanimstatelabels.h llappearance.h @@ -617,9 +628,8 @@ set(viewer_HEADER_FILES llfloatergroups.h llfloaterhandler.h llfloaterhardwaresettings.h - llfloaterhtml.h llfloaterhtmlcurrency.h - llfloaterhtmlhelp.h + llfloatermediabrowser.h llfloaterhtmlsimple.h llfloaterhud.h llfloaterimagepreview.h @@ -756,12 +766,14 @@ set(viewer_HEADER_FILES llpanelinventory.h llpanelimcontrolpanel.h llpanelland.h + llpanellandaudio.h llpanellandmarks.h llpanellandmedia.h llpanellogin.h llpanellookinfo.h llpanellooks.h llpanelmedia.h + llpanelmediahud.h llpanelmeprofile.h llpanelmovetip.h llpanelobject.h @@ -815,6 +827,7 @@ set(viewer_HEADER_FILES llsyswellwindow.h lltable.h llteleporthistory.h + llteleporthistorystorage.h lltexglobalcolor.h lltexlayer.h lltexlayerparams.h @@ -825,6 +838,7 @@ set(viewer_HEADER_FILES lltoast.h lltoastalertpanel.h lltoastgroupnotifypanel.h + lltoastimpanel.h lltoastnotifypanel.h lltoastpanel.h lltoggleablemenu.h @@ -876,6 +890,8 @@ set(viewer_HEADER_FILES llviewerkeyboard.h llviewerlayer.h llviewermedia.h + llviewermediaobserver.h + llviewermediafocus.h llviewermenu.h llviewermenufile.h llviewermessage.h @@ -930,7 +946,7 @@ set(viewer_HEADER_FILES llwearabledictionary.h llwearablelist.h llweb.h - llwebbrowserctrl.h + llmediactrl.h llwind.h llwindebug.h llwlanimator.h @@ -1131,6 +1147,9 @@ endif (WINDOWS) set(viewer_XUI_FILES skins/default/colors.xml skins/default/textures/textures.xml + + + ) file(GLOB DEFAULT_XUI_FILE_GLOB_LIST ${CMAKE_CURRENT_SOURCE_DIR}/skins/default/xui/en/*.xml) @@ -1376,6 +1395,9 @@ if (WINDOWS) --touch=${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/touched.bat DEPENDS ${VIEWER_BINARY_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) + + add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit media_plugin_flash_activex media_plugin_awesomium) + if (PACKAGE) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/event_host.tar.bz2 @@ -1407,8 +1429,8 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLIMAGE_LIBRARIES} ${LLIMAGEJ2COJ_LIBRARIES} ${LLINVENTORY_LIBRARIES} - ${LLMEDIA_LIBRARIES} ${LLMESSAGE_LIBRARIES} + ${LLPLUGIN_LIBRARIES} ${LLPRIMITIVE_LIBRARIES} ${LLRENDER_LIBRARIES} ${FREETYPE_LIBRARIES} @@ -1416,6 +1438,7 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${LLVFS_LIBRARIES} ${LLWINDOW_LIBRARIES} ${LLXML_LIBRARIES} + ${LLXUIXML_LIBRARIES} ${LSCRIPT_LIBRARIES} ${LLMATH_LIBRARIES} ${LLCOMMON_LIBRARIES} @@ -1427,11 +1450,9 @@ target_link_libraries(${VIEWER_BINARY_NAME} ${OPENGL_LIBRARIES} ${FMODWRAPPER_LIBRARY} ${OPENGL_LIBRARIES} - ${MOZLIB_LIBRARIES} ${SDL_LIBRARY} ${SMARTHEAP_LIBRARY} ${UI_LIBRARIES} - ${QUICKTIME_LIBRARY} ${WINDOWS_LIBRARIES} ${XMLRPCEPI_LIBRARIES} ${ELFIO_LIBRARIES} @@ -1473,9 +1494,12 @@ if (LINUX) DEPENDS secondlife-stripped ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) + add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_gstreamer010 media_plugin_webkit) + 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) @@ -1510,6 +1534,8 @@ if (DARWIN) DEPENDS ${VIEWER_BINARY_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/viewer_manifest.py ) + add_dependencies(${VIEWER_BINARY_NAME} SLPlugin media_plugin_quicktime media_plugin_webkit media_plugin_awesomium) + if (PACKAGE) add_custom_target(package ALL DEPENDS ${VIEWER_BINARY_NAME}) add_dependencies(package mac-updater mac-crash-logger) @@ -1576,3 +1602,79 @@ set_source_files_properties( LL_ADD_PROJECT_UNIT_TESTS(${VIEWER_BINARY_NAME} "${viewer_TEST_SOURCE_FILES}") #ADD_VIEWER_BUILD_TEST(llmemoryview viewer) + + +# Don't do these for DARWIN or LINUX here -- they're taken care of by viewer_manifest.py +if (WINDOWS) + get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_if_different + ${BUILT_SLPLUGIN} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin + COMMENT "Copying SLPlugin executable to the runtime folder." + ) + + get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION) + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_if_different + ${BUILT_WEBKIT_PLUGIN} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin + COMMENT "Copying WebKit Plugin to the runtime folder." + ) + + get_target_property(BUILT_QUICKTIME_PLUGIN media_plugin_quicktime LOCATION) + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_if_different + ${BUILT_QUICKTIME_PLUGIN} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin + COMMENT "Copying Quicktime Plugin to the runtime folder." + ) +endif (WINDOWS) + +if (WINDOWS) + get_target_property(BUILT_FLASH_ACTIVEX_PLUGIN media_plugin_flash_activex LOCATION) + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_if_different + ${BUILT_FLASH_ACTIVEX_PLUGIN} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin + COMMENT "Copying Flash (ActiveX) Plugin to the runtime folder." + ) +endif (WINDOWS) + +if (WINDOWS) + get_target_property(BUILT_AWESOMIUM_PLUGIN media_plugin_awesomium LOCATION) + add_custom_command( + TARGET ${VIEWER_BINARY_NAME} POST_BUILD + COMMAND ${CMAKE_COMMAND} + ARGS + -E + copy_if_different + ${BUILT_AWESOMIUM_PLUGIN} + ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin + COMMENT "Copying Flash (Awesomium) Plugin to the runtime folder." + ) +endif (WINDOWS) + +if (DARWIN) +# Don't do this here -- it's taken care of by viewer_manifest.py +# add_custom_command(TARGET ${VIEWER_BINARY_NAME} POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllwebkitlib.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/llplugin/ +# DEPENDS ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllwebkitlib.dylib +# ) +endif (DARWIN) diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 702839869a..2fe8fd5b00 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -166,17 +166,6 @@ <key>Value</key> <real>0.5</real> </map> - <key>AudioLevelDistance</key> - <map> - <key>Comment</key> - <string>Scale factor for audio engine (multiple of world scale, 2.0 = audio falls off twice as fast)</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>F32</string> - <key>Value</key> - <real>1.0</real> - </map> <key>AudioLevelDoppler</key> <map> <key>Comment</key> @@ -1673,6 +1662,17 @@ <key>Value</key> <integer>0</integer> </map> + <key>DebugPluginDisableTimeout</key> + <map> + <key>Comment</key> + <string>Disable the code which watches for plugins that are crashed or hung</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> <key>DebugShowColor</key> <map> <key>Comment</key> @@ -3216,7 +3216,7 @@ <key>Value</key> <array> <real>1.5</real> - <real>0.5</real> + <real>0.7</real> <real>1.0</real> </array> </map> @@ -4435,7 +4435,40 @@ <key>Value</key> <integer>0</integer> </map> - <key>MemoryLogFrequency</key> + <key>MediaControlFadeTime</key> + <map> + <key>Comment</key> + <string>Amount of time (in seconds) that the media control fades</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>1.5</real> + </map> + <key>MediaControlTimeout</key> + <map> + <key>Comment</key> + <string>Amount of time (in seconds) for media controls to fade with no mouse activity</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>F32</string> + <key>Value</key> + <real>3.0</real> + </map> + <key>MediaOnAPrimUI</key> + <map> + <key>Comment</key> + <string>Whether or not to show the "link sharing" UI</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>Boolean</string> + <key>Value</key> + <integer>0</integer> + </map> + <key>MemoryLogFrequency</key> <map> <key>Comment</key> <string>Seconds between display of Memory in log (0 for never)</string> @@ -7254,17 +7287,6 @@ <key>Value</key> <integer>0</integer> </map> - <key>ShowSearchBar</key> - <map> - <key>Comment</key> - <string>Show the Search Bar in the Status Overlay</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>Boolean</string> - <key>Value</key> - <integer>0</integer> - </map> <key>ShowNetStats</key> <map> <key>Comment</key> @@ -8164,6 +8186,50 @@ <key>Value</key> <string>5748decc-f629-461c-9a36-a35a221fe21f</string> </map> + <key>StartUpChannelUUID</key> + <map> + <key>Comment</key> + <string /> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>B56AF90D-6684-48E4-B1E4-722D3DEB2CB6</string> + </map> + <key>NearByChatChannelUUID</key> + <map> + <key>Comment</key> + <string /> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>E1158BD6-661C-4981-9DAD-4DCBFF062502</string> + </map> + <key>NotificationChannelUUID</key> + <map> + <key>Comment</key> + <string /> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>AEED3193-8709-4693-8558-7452CCA97AE5</string> + </map> + <key>AlertChannelUUID</key> + <map> + <key>Comment</key> + <string /> + <key>Persist</key> + <integer>0</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>F3E07BC8-A973-476D-8C7F-F3B7293975D1</string> + </map> <key>UIImgWhiteUUID</key> <map> <key>Comment</key> @@ -9738,16 +9804,5 @@ <key>Value</key> <integer>0</integer> </map> - <key>GroupTeleportMembersLimit</key> - <map> - <key>Comment</key> - <string>Max number of members of group to offer teleport</string> - <key>Persist</key> - <integer>1</integer> - <key>Type</key> - <string>U32</string> - <key>Value</key> - <integer>100</integer> - </map> </map> </llsd> diff --git a/indra/newview/build_win32_appConfig.py b/indra/newview/build_win32_appConfig.py index 06312bea26..8eadf0068f 100644 --- a/indra/newview/build_win32_appConfig.py +++ b/indra/newview/build_win32_appConfig.py @@ -28,7 +28,7 @@ # COMPLETENESS OR PERFORMANCE. # $/LicenseInfo$ -import sys, os +import sys, os, re from xml.dom.minidom import parse def munge_binding_redirect_version(src_manifest_name, src_config_name, dst_config_name): @@ -39,7 +39,8 @@ def munge_binding_redirect_version(src_manifest_name, src_config_name, dst_confi config_dom = parse(src_config_name) node = config_dom.getElementsByTagName('bindingRedirect')[0] node.setAttribute('newVersion', manifest_assm_ver) - node.setAttribute('oldVersion', node.getAttribute('oldVersion') + manifest_assm_ver) + src_old_ver = re.match('([^-]*-).*', node.getAttribute('oldVersion')).group(1) + node.setAttribute('oldVersion', src_old_ver + manifest_assm_ver) comment = config_dom.createComment("This file is automatically generated by the build. see indra/newview/build_win32_appConfig.py") config_dom.insertBefore(comment, config_dom.childNodes[0]) diff --git a/indra/newview/gpu_table.txt b/indra/newview/gpu_table.txt index 56de9c610a..733670f311 100644 --- a/indra/newview/gpu_table.txt +++ b/indra/newview/gpu_table.txt @@ -70,8 +70,8 @@ ATI M72 .*ATI.*M72.* 1 1 ATI M76 .*ATI.*M76.* 3 1 ATI Mobility Radeon 7xxx .*ATI.*Mobility.*Radeon 7.* 0 1 ATI Mobility Radeon 8xxx .*ATI.*Mobility.*Radeon 8.* 0 1 -ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 1 1 -ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 1 1 +ATI Mobility Radeon 9800 .*ATI.*Mobility.*98.* 0 1 +ATI Mobility Radeon 9700 .*ATI.*Mobility.*97.* 0 1 ATI Mobility Radeon 9600 .*ATI.*Mobility.*96.* 0 1 ATI Mobility Radeon HD 2300 .*ATI.*Mobility.*HD.*23.* 1 1 ATI Mobility Radeon HD 2400 .*ATI.*Mobility.*HD.*24.* 1 1 @@ -162,7 +162,10 @@ Intel Montara .*Intel.*Montara.* 0 0 Intel Springdale .*Intel.*Springdale.* 0 0 Matrox .*Matrox.* 0 0 Mesa .*Mesa.* 0 0 +NVIDIA GT 120 .*NVIDIA.*GeForce.*GT.*12.* 3 1 +NVIDIA GT 130 .*NVIDIA.*GeForce.*GT.*13.* 3 1 NVIDIA GTX 260 .*NVIDIA.*GeForce.*GTX.*26.* 3 1 +NVIDIA GTX 270 .*NVIDIA.*GeForce.*GTX.*27.* 3 1 NVIDIA GTX 280 .*NVIDIA.*GeForce.*GTX.*28.* 3 1 NVIDIA GTX 290 .*NVIDIA.*GeForce.*GTX.*29.* 3 1 NVIDIA C51 .*NVIDIA.*C51.* 0 1 @@ -263,7 +266,6 @@ NVIDIA Quadro FX 4500 .*Quadro.*FX.*4500.* 3 1 NVIDIA Quadro FX .*Quadro FX.* 1 1 NVIDIA Quadro NVS .*Quadro NVS.* 0 1 NVIDIA RIVA TNT .*RIVA TNT.* 0 0 -NVIDIA PCI .*NVIDIA.*/PCI/SSE2 0 0 S3 .*S3 Graphics.* 0 0 SiS SiS.* 0 0 Trident Trident.* 0 0 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 dfe19c1232..3209654498 100755 --- a/indra/newview/linux_tools/wrapper.sh +++ b/indra/newview/linux_tools/wrapper.sh @@ -91,7 +91,11 @@ 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. export SAVED_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" @@ -116,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/llagent.cpp b/indra/newview/llagent.cpp index 366c337269..a4692dd38b 100644 --- a/indra/newview/llagent.cpp +++ b/indra/newview/llagent.cpp @@ -85,6 +85,7 @@ #include "llviewercamera.h" #include "llviewerdisplay.h" +#include "llviewermediafocus.h" #include "llviewerobjectlist.h" #include "llviewerparcelmgr.h" #include "llviewerstats.h" @@ -105,6 +106,7 @@ #include "llcapabilitylistener.h" #include "llnavigationbar.h" //to show/hide navigation bar when changing mouse look state +#include "llagentui.h" using namespace LLVOAvatarDefines; @@ -916,6 +918,8 @@ void LLAgent::setRegion(LLViewerRegion *regionp) mRegionsVisited.insert(handle); LLSelectMgr::getInstance()->updateSelectionCenter(); + + LLFloaterMove::sUpdateFlyingStatus(); } @@ -941,42 +945,6 @@ LLHost LLAgent::getRegionHost() const } //----------------------------------------------------------------------------- -// getSLURL() -// returns empty() if getRegion() == NULL -//----------------------------------------------------------------------------- -std::string LLAgent::getSLURL() const -{ - return buildSLURL(true); -} - -//----------------------------------------------------------------------------- -// getUnescapedSLURL() -// returns empty() if getRegion() == NULL -//----------------------------------------------------------------------------- -std::string LLAgent::getUnescapedSLURL() const -{ - return buildSLURL(false); -} - -std::string LLAgent::buildSLURL(const bool escape) const -{ - std::string slurl; - LLViewerRegion *regionp = getRegion(); - if (regionp) - { - LLVector3d agentPos = getPositionGlobal(); - S32 x = llround( (F32)fmod( agentPos.mdV[VX], (F64)REGION_WIDTH_METERS ) ); - S32 y = llround( (F32)fmod( agentPos.mdV[VY], (F64)REGION_WIDTH_METERS ) ); - S32 z = llround( (F32)agentPos.mdV[VZ] ); - if (escape) - slurl = LLSLURL::buildSLURL(regionp->getName(), x, y, z); - else - slurl = LLSLURL::buildUnescapedSLURL(regionp->getName(), x, y, z); - } - return slurl; -} - -//----------------------------------------------------------------------------- // inPrelude() //----------------------------------------------------------------------------- BOOL LLAgent::inPrelude() @@ -2858,6 +2826,8 @@ void LLAgent::endAnimationUpdateUI() LLSideTray::getInstance()->setVisible(TRUE); + LLPanelStandStopFlying::getInstance()->setVisible(TRUE); + LLToolMgr::getInstance()->setCurrentToolset(gBasicToolset); LLFloaterCamera::toPrevModeIfInAvatarViewMode(); @@ -2950,6 +2920,8 @@ void LLAgent::endAnimationUpdateUI() LLSideTray::getInstance()->setVisible(FALSE); + LLPanelStandStopFlying::getInstance()->setVisible(FALSE); + // clear out camera lag effect mCameraLag.clearVec(); @@ -5066,14 +5038,7 @@ void LLAgent::handleMaturity(const LLSD& newvalue) //---------------------------------------------------------------------------- -void LLAgent::buildFullname(std::string& name) const -{ - if (mAvatarObject.notNull()) - { - name = mAvatarObject->getFullname(); - } -} - +//*TODO remove, is not used anywhere as of August 20, 2009 void LLAgent::buildFullnameAndTitle(std::string& name) const { if (isGroupMember()) @@ -5227,90 +5192,6 @@ BOOL LLAgent::setUserGroupFlags(const LLUUID& group_id, BOOL accept_notices, BOO return FALSE; } -// utility to build a location string -BOOL LLAgent::buildLocationString(std::string& str, ELocationFormat fmt) -{ - LLViewerRegion* region = getRegion(); - LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); - - if (!region || !parcel) - return FALSE; - - const LLVector3& agent_pos_region = getPositionAgent(); - S32 pos_x = S32(agent_pos_region.mV[VX]); - S32 pos_y = S32(agent_pos_region.mV[VY]); - S32 pos_z = S32(agent_pos_region.mV[VZ]); - - // Round the numbers based on the velocity - LLVector3 agent_velocity = getVelocity(); - F32 velocity_mag_sq = agent_velocity.magVecSquared(); - - const F32 FLY_CUTOFF = 6.f; // meters/sec - const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; - const F32 WALK_CUTOFF = 1.5f; // meters/sec - const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; - - if (velocity_mag_sq > FLY_CUTOFF_SQ) - { - pos_x -= pos_x % 4; - pos_y -= pos_y % 4; - } - else if (velocity_mag_sq > WALK_CUTOFF_SQ) - { - pos_x -= pos_x % 2; - pos_y -= pos_y % 2; - } - - // create a defult name and description for the landmark - std::string parcel_name = LLViewerParcelMgr::getInstance()->getAgentParcelName(); - std::string region_name = region->getName(); - std::string buffer; - if( LLViewerParcelMgr::getInstance()->getAgentParcelName().empty() ) - { - // the parcel doesn't have a name - switch (fmt) - { - case LOCATION_FORMAT_LANDMARK: - buffer = llformat("%.100s", region_name.c_str()); - break; - case LOCATION_FORMAT_NORMAL: - buffer = llformat("%s", region_name.c_str()); - break; - case LOCATION_FORMAT_FULL: - buffer = llformat("%s (%d, %d, %d)", - region_name.c_str(), - pos_x, pos_y, pos_z); - break; - } - } - else - { - // the parcel has a name, so include it in the landmark name - switch (fmt) - { - case LOCATION_FORMAT_LANDMARK: - buffer = llformat("%.100s", parcel_name.c_str()); - break; - case LOCATION_FORMAT_NORMAL: - buffer = llformat("%s, %s", - region_name.c_str(), - parcel_name.c_str()); - break; - case LOCATION_FORMAT_FULL: - std::string sim_access_string = region->getSimAccessString(); - buffer = llformat("%s, %s (%d, %d, %d)%s%s", - region_name.c_str(), - parcel_name.c_str(), - pos_x, pos_y, pos_z, - sim_access_string.empty() ? "" : " - ", - sim_access_string.c_str()); - break; - } - } - str = buffer; - return TRUE; -} - LLQuaternion LLAgent::getHeadRotation() { if (mAvatarObject.isNull() || !mAvatarObject->mPelvisp || !mAvatarObject->mHeadp) @@ -5472,30 +5353,6 @@ BOOL LLAgent::allowOperation(PermissionBit op, return perm.allowOperationBy(op, agent_proxy, group_proxy); } - -void LLAgent::getName(std::string& name) const -{ - name.clear(); - - if (mAvatarObject.notNull()) - { - LLNameValue *first_nv = mAvatarObject->getNVPair("FirstName"); - LLNameValue *last_nv = mAvatarObject->getNVPair("LastName"); - if (first_nv && last_nv) - { - name = first_nv->printData() + " " + last_nv->printData(); - } - else - { - llwarns << "Agent is missing FirstName and/or LastName nv pair." << llendl; - } - } - else - { - name = gSavedSettings.getString("FirstName") + " " + gSavedSettings.getString("LastName"); - } -} - const LLColor4 &LLAgent::getEffectColor() { return mEffectColor; @@ -6141,6 +5998,7 @@ bool LLAgent::teleportCore(bool is_local) LLFloaterReg::hideInstance("about_land"); LLViewerParcelMgr::getInstance()->deselectLand(); + LLViewerMediaFocus::getInstance()->setFocusFace(false, NULL, 0, NULL); // Close all pie menus, deselect land, etc. // Don't change the camera until we know teleport succeeded. JC @@ -6305,12 +6163,12 @@ void LLAgent::setTeleportState(ETeleportState state) if (mTeleportState == TELEPORT_MOVING) { // We're outa here. Save "back" slurl. - mTeleportSourceSLURL = getSLURL(); + mTeleportSourceSLURL = LLAgentUI::buildSLURL(); } else if(mTeleportState == TELEPORT_ARRIVING) { // Let the interested parties know we've teleported. - LLViewerParcelMgr::getInstance()->onTeleportFinished(); + LLViewerParcelMgr::getInstance()->onTeleportFinished(false, getPositionGlobal()); } } diff --git a/indra/newview/llagent.h b/indra/newview/llagent.h index 090f165576..7e845cafb7 100644 --- a/indra/newview/llagent.h +++ b/indra/newview/llagent.h @@ -173,8 +173,7 @@ public: // Name //-------------------------------------------------------------------- public: - void getName(std::string& name) const; - void buildFullname(std::string &name) const; + //*TODO remove, is not used as of August 20, 2009 void buildFullnameAndTitle(std::string &name) const; //-------------------------------------------------------------------- @@ -272,18 +271,15 @@ public: { LOCATION_FORMAT_NORMAL, LOCATION_FORMAT_LANDMARK, + LOCATION_FORMAT_WITHOUT_SIM, LOCATION_FORMAT_FULL, }; void setRegion(LLViewerRegion *regionp); LLViewerRegion *getRegion() const; LLHost getRegionHost() const; - std::string getSLURL() const; - std::string getUnescapedSLURL() const; BOOL inPrelude(); - BOOL buildLocationString(std::string& str, ELocationFormat fmt = LOCATION_FORMAT_LANDMARK); // Utility to build a location string private: LLViewerRegion *mRegionp; - std::string buildSLURL(const bool escape) const; //-------------------------------------------------------------------- // History diff --git a/indra/newview/llagentui.cpp b/indra/newview/llagentui.cpp new file mode 100644 index 0000000000..93d88bf3df --- /dev/null +++ b/indra/newview/llagentui.cpp @@ -0,0 +1,179 @@ +/** + * @file llagentui.cpp + * @brief Utility methods to process agent's data as slurl's etc. before displaying + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 "llviewerprecompiledheaders.h" + +#include "llagentui.h" + +// Library includes +#include "llparcel.h" + +// Viewer includes +#include "llagent.h" +#include "llviewercontrol.h" +#include "llviewerregion.h" +#include "llviewerparcelmgr.h" +#include "llvoavatarself.h" +#include "llslurl.h" + +//static +void LLAgentUI::buildName(std::string& name) +{ + name.clear(); + + LLVOAvatarSelf* avatar_object = gAgent.getAvatarObject(); + if (avatar_object) + { + LLNameValue *first_nv = avatar_object->getNVPair("FirstName"); + LLNameValue *last_nv = avatar_object->getNVPair("LastName"); + if (first_nv && last_nv) + { + name = first_nv->printData() + " " + last_nv->printData(); + } + else + { + llwarns << "Agent is missing FirstName and/or LastName nv pair." << llendl; + } + } + else + { + name = gSavedSettings.getString("FirstName") + " " + gSavedSettings.getString("LastName"); + } +} + +//static +void LLAgentUI::buildFullname(std::string& name) +{ + if (gAgent.getAvatarObject()) name = gAgent.getAvatarObject()->getFullname(); +} + +//static +std::string LLAgentUI::buildSLURL(const bool escaped /*= true*/) +{ + std::string slurl; + LLViewerRegion *regionp = gAgent.getRegion(); + if (regionp) + { + LLVector3d agentPos = gAgent.getPositionGlobal(); + slurl = LLSLURL::buildSLURLfromPosGlobal(regionp->getName(), agentPos, escaped); + } + return slurl; +} + +BOOL LLAgentUI::buildLocationString(std::string& str, LLAgent::ELocationFormat fmt,const LLVector3& agent_pos_region) +{ + LLViewerRegion* region = gAgent.getRegion(); + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + + if (!region || !parcel) return FALSE; + + S32 pos_x = S32(agent_pos_region.mV[VX]); + S32 pos_y = S32(agent_pos_region.mV[VY]); + S32 pos_z = S32(agent_pos_region.mV[VZ]); + + // Round the numbers based on the velocity + F32 velocity_mag_sq = gAgent.getVelocity().magVecSquared(); + + const F32 FLY_CUTOFF = 6.f; // meters/sec + const F32 FLY_CUTOFF_SQ = FLY_CUTOFF * FLY_CUTOFF; + const F32 WALK_CUTOFF = 1.5f; // meters/sec + const F32 WALK_CUTOFF_SQ = WALK_CUTOFF * WALK_CUTOFF; + + if (velocity_mag_sq > FLY_CUTOFF_SQ) + { + pos_x -= pos_x % 4; + pos_y -= pos_y % 4; + } + else if (velocity_mag_sq > WALK_CUTOFF_SQ) + { + pos_x -= pos_x % 2; + pos_y -= pos_y % 2; + } + + // create a default name and description for the landmark + std::string parcel_name = LLViewerParcelMgr::getInstance()->getAgentParcelName(); + std::string region_name = region->getName(); + std::string buffer; + if( parcel_name.empty() ) + { + // the parcel doesn't have a name + switch (fmt) + { + case LLAgent::LOCATION_FORMAT_LANDMARK: + buffer = llformat("%.100s", region_name.c_str()); + break; + case LLAgent::LOCATION_FORMAT_NORMAL: + buffer = llformat("%s", region_name.c_str()); + break; + case LLAgent::LOCATION_FORMAT_WITHOUT_SIM: + case LLAgent::LOCATION_FORMAT_FULL: + buffer = llformat("%s (%d, %d, %d)", + region_name.c_str(), + pos_x, pos_y, pos_z); + break; + } + } + else + { + // the parcel has a name, so include it in the landmark name + switch (fmt) + { + case LLAgent::LOCATION_FORMAT_LANDMARK: + buffer = llformat("%.100s", parcel_name.c_str()); + break; + case LLAgent::LOCATION_FORMAT_NORMAL: + buffer = llformat("%s, %s", region_name.c_str(), parcel_name.c_str()); + break; + case LLAgent::LOCATION_FORMAT_WITHOUT_SIM: + buffer = llformat("%s, %s (%d, %d, %d)", + region_name.c_str(), + parcel_name.c_str(), + pos_x, pos_y, pos_z); + break; + case LLAgent::LOCATION_FORMAT_FULL: + std::string sim_access_string = region->getSimAccessString(); + buffer = llformat("%s, %s (%d, %d, %d)%s%s", + region_name.c_str(), + parcel_name.c_str(), + pos_x, pos_y, pos_z, + sim_access_string.empty() ? "" : " - ", + sim_access_string.c_str()); + break; + } + } + str = buffer; + return TRUE; +} +BOOL LLAgentUI::buildLocationString(std::string& str, LLAgent::ELocationFormat fmt) +{ + return buildLocationString(str,fmt, gAgent.getPositionAgent()); +} diff --git a/indra/newview/llagentui.h b/indra/newview/llagentui.h new file mode 100644 index 0000000000..c3bc58d791 --- /dev/null +++ b/indra/newview/llagentui.h @@ -0,0 +1,51 @@ +/** + * @file llagentui.h + * @brief Utility methods to process agent's data as slurl's etc. before displaying + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 LLAGENTUI_H +#define LLAGENTUI_H + +#include "llagent.h" + +class LLAgentUI +{ +public: + static void buildName(std::string& name); + static void buildFullname(std::string &name); + + static std::string buildSLURL(const bool escaped = true); + //build location string using the current position of gAgent. + static BOOL buildLocationString(std::string& str, LLAgent::ELocationFormat fmt = LLAgent::LOCATION_FORMAT_LANDMARK); + //build location string using a region position of the avatar. + static BOOL buildLocationString(std::string& str, LLAgent::ELocationFormat fmt,const LLVector3& agent_pos_region); +}; + +#endif //LLAGENTUI_H diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index 4834b31bc7..0f735282cb 100644 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -44,12 +44,42 @@ #include "llvoavatarself.h" #include "llwearable.h" #include "llwearablelist.h" +#include "llgesturemgr.h" #include <boost/scoped_ptr.hpp> -// For viewer2.0 internal demo, don't use current outfit folder contents at all during initial startup. Will reenable -// this once we're sure this works completely. -// #define USE_CURRENT_OUTFIT_FOLDER +#define USE_CURRENT_OUTFIT_FOLDER + +//-------------------------------------------------------------------- +// Classes for fetching initial wearables data +//-------------------------------------------------------------------- +// Outfit folder fetching callback structure. +class LLInitialWearablesFetch : public LLInventoryFetchDescendentsObserver +{ +public: + LLInitialWearablesFetch() {} + ~LLInitialWearablesFetch() {} + virtual void done(); + + struct InitialWearableData + { + EWearableType mType; + U32 mIndex; + LLUUID mItemID; + LLUUID mAssetID; + InitialWearableData(EWearableType type, U32 index, LLUUID itemID, LLUUID assetID) : + mType(type), mIndex(index), mItemID(itemID), mAssetID(assetID) { } + }; + + typedef std::vector<InitialWearableData> initial_wearable_data_vec_t; + initial_wearable_data_vec_t mCOFInitialWearables; // Wearables from the Current Outfit Folder + initial_wearable_data_vec_t mAgentInitialWearables; // Wearables from the old agent wearables msg + +protected: + void processInitialWearables(); +}; + + LLAgentWearables gAgentWearables; @@ -668,6 +698,9 @@ BOOL LLAgentWearables::isWearingItem(const LLUUID& item_id, BOOL include_linked_ // MULTI-WEARABLE: update for multiple // static +// ! BACKWARDS COMPATIBILITY ! When we stop supporting viewer1.23, we can assume +// that viewers have a Current Outfit Folder and won't need this message, and thus +// we can remove/ignore this whole function. void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data) { // We should only receive this message a single time. Ignore subsequent AgentWearablesUpdates @@ -696,7 +729,7 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs // Get the UUID of the current outfit folder (will be created if it doesn't exist) LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); - LLOutfitFolderFetch* outfit = new LLOutfitFolderFetch(); + LLInitialWearablesFetch* outfit = new LLInitialWearablesFetch(); //lldebugs << "processAgentInitialWearablesUpdate()" << llendl; // Add wearables @@ -733,8 +766,8 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs // MULTI-WEARABLE: TODO: update once messages change. Currently use results to populate the zeroth element. // Store initial wearables data until we know whether we have the current outfit folder or need to use the data. - InitialWearableData * temp_wearable_data = new InitialWearableData(type, 0, item_id, asset_id); // MULTI-WEARABLE: update - outfit->mAgentInitialWearables.push_back(temp_wearable_data); + LLInitialWearablesFetch::InitialWearableData wearable_data(type, 0, item_id, asset_id); // MULTI-WEARABLE: update + outfit->mAgentInitialWearables.push_back(wearable_data); } @@ -761,52 +794,11 @@ void LLAgentWearables::processAgentInitialWearablesUpdate(LLMessageSystem* mesgs } } -// static -void LLAgentWearables::fetchInitialWearables(initial_wearable_data_vec_t & current_outfit_links, initial_wearable_data_vec_t & message_wearables) -{ -#ifdef USE_CURRENT_OUTFIT_FOLDER - if (!current_outfit_links.empty()) - { - for (U8 i = 0; i < current_outfit_links.size(); ++i) - { - // Fetch the wearables in the current outfit folder - LLWearableList::instance().getAsset(current_outfit_links[i]->mAssetID, - LLStringUtil::null, - LLWearableDictionary::getAssetType(current_outfit_links[i]->mType), - onInitialWearableAssetArrived, (void*)(current_outfit_links[i])); - } - } - else -#endif - if (!message_wearables.empty()) // We have an empty current outfit folder, use the message data instead. - { - LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); - for (U8 i = 0; i < message_wearables.size(); ++i) - { - // Populate the current outfit folder with links to the wearables passed in the message -#ifdef USE_CURRENT_OUTFIT_FOLDER - std::string link_name = "WearableLink"; - link_inventory_item(gAgent.getID(), message_wearables[i]->mItemID, current_outfit_id, link_name, - LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL)); -#endif - // Fetch the wearables - LLWearableList::instance().getAsset(message_wearables[i]->mAssetID, - LLStringUtil::null, - LLWearableDictionary::getAssetType(message_wearables[i]->mType), - onInitialWearableAssetArrived, (void*)(message_wearables[i])); - } - } - else - { - LL_WARNS("Wearables") << "No current outfit folder iterms found and no initial wearables fallback message received." << LL_ENDL; - } -} - // A single wearable that the avatar was wearing on start-up has arrived from the database. // static void LLAgentWearables::onInitialWearableAssetArrived(LLWearable* wearable, void* userdata) { - boost::scoped_ptr<InitialWearableData> wear_data((InitialWearableData*)userdata); + boost::scoped_ptr<LLInitialWearablesFetch::InitialWearableData> wear_data((LLInitialWearablesFetch::InitialWearableData*)userdata); const EWearableType type = wear_data->mType; const U32 index = wear_data->mIndex; @@ -999,6 +991,18 @@ void LLAgentWearables::createStandardWearablesAllDone() mAvatarObject->onFirstTEMessageReceived(); } +void LLAgentWearables::getAllWearablesArray(LLDynamicArray<S32>& wearables) +{ + for( S32 i = 0; i < WT_COUNT; ++i ) + { + // MULTI-WEARABLE: Properly handle multiwearables later. + if (getWearable( (EWearableType) i, 0 ) != NULL) + { + wearables.push_back(i); + } + } +} + // Note: wearables_to_include should be a list of EWearableType types // attachments_to_include should be a list of attachment points void LLAgentWearables::makeNewOutfit(const std::string& new_folder_name, @@ -1134,16 +1138,31 @@ void LLAgentWearables::makeNewOutfit(const std::string& new_folder_name, } } +LLUUID LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name) +{ + if (mAvatarObject.isNull()) + { + return LLUUID::null; + } + + LLDynamicArray<S32> wearables_to_include; + getAllWearablesArray(wearables_to_include); + + LLDynamicArray<S32> attachments_to_include; + mAvatarObject->getAllAttachmentsArray(attachments_to_include); + + return makeNewOutfitLinks(new_folder_name, wearables_to_include, attachments_to_include); +} + // Note: wearables_to_include should be a list of EWearableType types // attachments_to_include should be a list of attachment points -void LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name, - const LLDynamicArray<S32>& wearables_to_include, - const LLDynamicArray<S32>& attachments_to_include, - BOOL rename_clothing) +LLUUID LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name, + const LLDynamicArray<S32>& wearables_to_include, + const LLDynamicArray<S32>& attachments_to_include) { if (mAvatarObject.isNull()) { - return; + return LLUUID::null; } // First, make a folder in the Clothes directory. @@ -1172,22 +1191,13 @@ void LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name, LLWearable* old_wearable = getWearable((EWearableType)type,j); if (old_wearable) { - std::string new_name; - if (rename_clothing) - { - new_name = new_folder_name; - new_name.append(" "); - new_name.append(old_wearable->getTypeLabel()); - LLStringUtil::truncate(new_name, DB_INV_ITEM_NAME_STR_LEN); - } - LLViewerInventoryItem* item = gInventory.getItem(getWearableItemID((EWearableType) type, j)); if (!item) continue; LLPointer<LLInventoryCallback> cb = NULL; link_inventory_item(gAgent.getID(), item->getUUID(), folder_id, - new_name, + item->getName(), LLAssetType::AT_LINK, cb); } @@ -1223,6 +1233,32 @@ void LLAgentWearables::makeNewOutfitLinks(const std::string& new_folder_name, cb); } } + + /////////////////// + // Gestures + + /* Disabling this for now, otherwise this adds all your default gestures and all previous + active gestures. Need to rethink the intended behavior. + for (LLGestureManager::item_map_t::iterator iter = LLGestureManager::instance().mActive.begin(); + iter != LLGestureManager::instance().mActive.end(); + ++iter) + { + const LLUUID &gesture_id = (*iter).first; + LLViewerInventoryItem* item = gInventory.getItem(gesture_id); + if (item) + { + LLPointer<LLInventoryCallback> cb = NULL; + link_inventory_item(gAgent.getID(), + item->getUUID(), + folder_id, + item->getName(), + LLAssetType::AT_LINK, + cb); + } + } + */ + + return folder_id; } void LLAgentWearables::makeNewOutfitDone(S32 type, U32 index) @@ -1774,55 +1810,101 @@ void LLAgentWearables::updateServer() gAgent.sendAgentSetAppearance(); } -void LLAgentWearables::LLOutfitFolderFetch::done() +void LLInitialWearablesFetch::done() { - // What we do here is get the complete information on the items in - // the library, and set up an observer that will wait for that to - // happen. + // Get the complete information on the items in the library, + // and set up an observer that will wait for that to happen. LLInventoryModel::cat_array_t cat_array; LLInventoryModel::item_array_t item_array; - gInventory.collectDescendents(mCompleteFolders.front(), - cat_array, - item_array, - LLInventoryModel::EXCLUDE_TRASH); + + LLFindWearables is_wearable; + gInventory.collectDescendentsIf(mCompleteFolders.front(), + cat_array, + item_array, + LLInventoryModel::EXCLUDE_TRASH, + is_wearable); S32 count = item_array.count(); - LLAgentWearables::initial_wearable_data_vec_t current_outfit_links; - current_outfit_links.reserve(count); + mCOFInitialWearables.reserve(count); - for(S32 i = 0; i < count; ++i) - { - // A bit of a hack since wearables database doesn't contain asset types... - // Perform indirection in case this assetID is in fact a link. This only works - // because of the assumption that all assetIDs and itemIDs are unique (i.e. - // no assetID is also used as an itemID elsewhere); therefore if the assetID - // exists as an itemID in the user's inventory, then this must be a link. - const LLInventoryItem *linked_item = gInventory.getItem(item_array.get(i)->getUUID()); - LLAssetType::EType asset_type = (LLAssetType::EType) 0; - if (linked_item) + for (LLInventoryModel::item_array_t::const_iterator iter = item_array.begin(); + iter != item_array.end(); + iter++) + { + const LLViewerInventoryItem *item = (*iter).get(); + // We're only concerned with linked items in the COF. Ignore + // any non-link items or links to folders. + if (item->getActualType() != LLAssetType::AT_LINK) + { + continue; + } + EWearableType type = (EWearableType) (item->getFlags() & LLInventoryItem::II_FLAGS_WEARABLES_MASK); + // MULTI-WEARABLE: update + InitialWearableData wearable_data(type, 0, item->getUUID(), item->getAssetUUID()); + mCOFInitialWearables.push_back(wearable_data); + } + + gInventory.removeObserver(this); + processInitialWearables(); + delete this; +} + +// This will either grab the contents of the Current Outfit Folder if they exist, +// or use the old-style initial agent wearables message. +void LLInitialWearablesFetch::processInitialWearables() +{ +#ifdef USE_CURRENT_OUTFIT_FOLDER + if (!mCOFInitialWearables.empty()) + { + for (U8 i = 0; i < mCOFInitialWearables.size(); ++i) { - asset_type = linked_item->getType(); - LLInventoryItem * base_item = gInventory.getItem(linked_item->getLinkedUUID()); - if (base_item) + // Fetch the wearables in the current outfit folder + InitialWearableData *wearable_data = new InitialWearableData(mCOFInitialWearables[i]); // This will be deleted in the callback. + if (wearable_data->mAssetID.notNull()) { - EWearableType type = (EWearableType) (base_item->getFlags() & LLInventoryItem::II_FLAGS_WEARABLES_MASK); - // MULTI-WEARABLE: update - InitialWearableData * temp_wearable_data = new InitialWearableData(type, 0, linked_item->getLinkedUUID(), base_item->getAssetUUID()); - current_outfit_links.push_back(temp_wearable_data); + LLWearableList::instance().getAsset(wearable_data->mAssetID, + LLStringUtil::null, + LLWearableDictionary::getAssetType(wearable_data->mType), + LLAgentWearables::onInitialWearableAssetArrived, (void*)(wearable_data)); } else { - llwarns << "Null base_item in LLOutfitFolderFetch::done, linkedUUID is " << linked_item->getLinkedUUID().asString() << llendl; + llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID " + << wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl; } } - else + } + else +#endif + if (!mAgentInitialWearables.empty()) // We have an empty current outfit folder, use the message data instead. + { + LLUUID current_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); + for (U8 i = 0; i < mAgentInitialWearables.size(); ++i) { - llwarns << "Null linked_item in LLOutfitFolderFetch::done, UUID is " << item_array.get(i)->getUUID().asString() << llendl; + // Populate the current outfit folder with links to the wearables passed in the message + InitialWearableData *wearable_data = new InitialWearableData(mAgentInitialWearables[i]); // This will be deleted in the callback. + + if (wearable_data->mAssetID.notNull()) + { +#ifdef USE_CURRENT_OUTFIT_FOLDER + const std::string link_name = "WearableLink"; // Unimportant what this is named, it isn't exposed. + link_inventory_item(gAgent.getID(), wearable_data->mItemID, current_outfit_id, link_name, + LLAssetType::AT_LINK, LLPointer<LLInventoryCallback>(NULL)); +#endif + // Fetch the wearables + LLWearableList::instance().getAsset(wearable_data->mAssetID, + LLStringUtil::null, + LLWearableDictionary::getAssetType(wearable_data->mType), + LLAgentWearables::onInitialWearableAssetArrived, (void*)(wearable_data)); + } + else + { + llinfos << "Invalid wearable, type " << wearable_data->mType << " itemID " + << wearable_data->mItemID << " assetID " << wearable_data->mAssetID << llendl; + } } } - - gInventory.removeObserver(this); - LLAgentWearables::fetchInitialWearables(current_outfit_links, mAgentInitialWearables); - mAgentInitialWearables.clear(); - delete this; + else + { + LL_WARNS("Wearables") << "No current outfit folder items found and no initial wearables fallback message received." << LL_ENDL; + } } - diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 971fd9ee37..b415ef9eb3 100644 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -43,30 +43,16 @@ class LLInventoryItem; class LLVOAvatarSelf; class LLWearable; - -// Forward Declaration -class LLInventoryFetchDescendentsObserver; +class LLInitialWearablesFetch; class LLAgentWearables { //-------------------------------------------------------------------- - // Data Types - //-------------------------------------------------------------------- - typedef struct _InitialWearableData - { - EWearableType mType; - U32 mIndex; - LLUUID mItemID; - LLUUID mAssetID; - _InitialWearableData(EWearableType type, U32 index, LLUUID itemID, LLUUID assetID) : - mType(type), mIndex(index), mItemID(itemID), mAssetID(assetID) { } - } InitialWearableData; - typedef std::vector<InitialWearableData *> initial_wearable_data_vec_t; - - //-------------------------------------------------------------------- // Constructors / destructors / Initializers //-------------------------------------------------------------------- public: + friend class LLInitialWearablesFetch; + LLAgentWearables(); virtual ~LLAgentWearables(); void setAvatarObject(LLVOAvatarSelf *avatar); @@ -149,7 +135,6 @@ protected: public: // Processes the initial wearables update message (if necessary, since the outfit folder makes it redundant) static void processAgentInitialWearablesUpdate(LLMessageSystem* mesgsys, void** user_data); - static void fetchInitialWearables(initial_wearable_data_vec_t & current_outfit_links, initial_wearable_data_vec_t & message_wearables); protected: void sendAgentWearablesUpdate(); void sendAgentWearablesRequest(); @@ -161,6 +146,8 @@ protected: // Outfits //-------------------------------------------------------------------- public: + void getAllWearablesArray(LLDynamicArray<S32>& wearables); + // Note: wearables_to_include should be a list of EWearableType types // attachments_to_include should be a list of attachment points void makeNewOutfit(const std::string& new_folder_name, @@ -170,10 +157,10 @@ public: // Note: wearables_to_include should be a list of EWearableType types // attachments_to_include should be a list of attachment points - void makeNewOutfitLinks(const std::string& new_folder_name, + LLUUID makeNewOutfitLinks(const std::string& new_folder_name); + LLUUID makeNewOutfitLinks(const std::string& new_folder_name, const LLDynamicArray<S32>& wearables_to_include, - const LLDynamicArray<S32>& attachments_to_include, - BOOL rename_clothing); + const LLDynamicArray<S32>& attachments_to_include); private: void makeNewOutfitDone(S32 type, U32 index); @@ -254,17 +241,6 @@ private: U32 mTodo; LLPointer<LLRefCount> mCB; }; - - // Outfit folder fetching callback structure. - class LLOutfitFolderFetch : public LLInventoryFetchDescendentsObserver - { - public: - LLOutfitFolderFetch() {} - ~LLOutfitFolderFetch() {} - virtual void done(); - - LLAgentWearables::initial_wearable_data_vec_t mAgentInitialWearables; - }; }; // LLAgentWearables diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 92db908059..dd00001cd4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -62,6 +62,8 @@ #include "llviewerwindow.h" #include "llviewerdisplay.h" #include "llviewermedia.h" +#include "llviewerparcelmedia.h" +#include "llviewermediafocus.h" #include "llviewermessage.h" #include "llviewerobjectlist.h" #include "llworldmap.h" @@ -114,10 +116,12 @@ #include "llassetstorage.h" #include "llpolymesh.h" #include "llcachename.h" -#include "audioengine.h" +#include "llaudioengine.h" +#include "llstreamingaudio.h" #include "llviewermenu.h" #include "llselectmgr.h" #include "lltrans.h" +#include "lltransutil.h" #include "lltracker.h" #include "llviewerparcelmgr.h" #include "llworldmapview.h" @@ -293,9 +297,9 @@ 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("APP_NAME"); default_trans_args.insert("SECOND_LIFE_GRID"); - default_trans_args.insert("SECOND_LIFE_SUPPORT"); + default_trans_args.insert("SUPPORT_SITE"); } //---------------------------------------------------------------------------- @@ -681,8 +685,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, @@ -871,6 +875,11 @@ bool LLAppViewer::init() return true; } +static LLFastTimer::DeclareTimer FTM_MESSAGES("System Messages"); +static LLFastTimer::DeclareTimer FTM_SLEEP("Sleep"); +static LLFastTimer::DeclareTimer FTM_IDLE("Idle"); +static LLFastTimer::DeclareTimer FTM_PUMP("Pump"); + bool LLAppViewer::mainLoop() { LLMemType mt1(LLMemType::MTYPE_MAIN); @@ -913,7 +922,7 @@ bool LLAppViewer::mainLoop() if (gViewerWindow) { - LLFastTimer t2(LLFastTimer::FTM_MESSAGES); + LLFastTimer t2(FTM_MESSAGES); gViewerWindow->mWindow->processMiscNativeEvents(); } @@ -921,7 +930,7 @@ bool LLAppViewer::mainLoop() if (gViewerWindow) { - LLFastTimer t2(LLFastTimer::FTM_MESSAGES); + LLFastTimer t2(FTM_MESSAGES); if (!restoreErrorTrap()) { llwarns << " Someone took over my signal/exception handler (post messagehandling)!" << llendl; @@ -971,14 +980,14 @@ bool LLAppViewer::mainLoop() { pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds! - LLFastTimer t3(LLFastTimer::FTM_IDLE); + LLFastTimer t3(FTM_IDLE); idle(); if (gAres != NULL && gAres->isInitialized()) { LLMemType mt_ip(LLMemType::MTYPE_IDLE_PUMP); pingMainloopTimeout("Main:ServicePump"); - LLFastTimer t4(LLFastTimer::FTM_PUMP); + LLFastTimer t4(FTM_PUMP); gAres->process(); // this pump is necessary to make the login screen show up gServicePump->pump(); @@ -1015,7 +1024,7 @@ bool LLAppViewer::mainLoop() // Sleep and run background threads { LLMemType mt_sleep(LLMemType::MTYPE_SLEEP); - LLFastTimer t2(LLFastTimer::FTM_SLEEP); + LLFastTimer t2(FTM_SLEEP); bool run_multiple_threads = gSavedSettings.getBOOL("RunMultipleThreads"); // yield some time to the os based on command line option @@ -1234,6 +1243,14 @@ bool LLAppViewer::cleanup() if (gAudiop) { + // shut down the streaming audio sub-subsystem first, in case it relies on not outliving the general audio subsystem. + + LLStreamingAudioInterface *sai = gAudiop->getStreamingAudioImpl(); + delete sai; + gAudiop->setStreamingAudioImpl(NULL); + + // shut down the audio subsystem + bool want_longname = false; if (gAudiop->getDriverName(want_longname) == "FMOD") { @@ -1467,7 +1484,9 @@ bool LLAppViewer::cleanup() //Note: //LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() //because some new image might be generated during cleaning up media. --bao + LLViewerMediaFocus::cleanupClass(); LLViewerMedia::cleanupClass(); + LLViewerParcelMedia::cleanupClass(); gTextureList.shutdown(); // shutdown again in case a callback added something LLUIImageList::getInstance()->cleanUp(); @@ -1763,8 +1782,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", @@ -2047,7 +2066,7 @@ bool LLAppViewer::initConfiguration() #if LL_DARWIN // Initialize apple menubar and various callbacks - init_apple_menu(LLTrans::getString("SECOND_LIFE_VIEWER").c_str()); + init_apple_menu(LLTrans::getString("APP_NAME").c_str()); #if __ppc__ // If the CPU doesn't have Altivec (i.e. it's not at least a G4), don't go any further. @@ -2086,7 +2105,7 @@ bool LLAppViewer::initConfiguration() // // Set the name of the window // - gWindowTitle = LLTrans::getString("SECOND_LIFE_VIEWER"); + gWindowTitle = LLTrans::getString("APP_NAME"); #if LL_DEBUG gWindowTitle += std::string(" [DEBUG] ") + gArgs; #else @@ -2203,7 +2222,7 @@ void LLAppViewer::checkForCrash(void) { std::ostringstream msg; msg << LLTrans::getString("MBFrozenCrashed"); - std::string alert = LLTrans::getString("SECOND_LIFE_VIEWER") + " " + LLTrans::getString("MBAlert"); + std::string alert = LLTrans::getString("APP_NAME") + " " + LLTrans::getString("MBAlert"); choice = OSMessageBox(msg.str(), alert, OSMB_YESNO); @@ -2413,7 +2432,7 @@ void LLAppViewer::writeSystemInfo() gDebugInfo["CrashNotHandled"] = (LLSD::Boolean)true; // Dump some debugging info - LL_INFOS("SystemInfo") << LLTrans::getString("SECOND_LIFE_VIEWER") + LL_INFOS("SystemInfo") << LLTrans::getString("APP_NAME") << " version " << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH << LL_ENDL; @@ -3091,7 +3110,7 @@ void LLAppViewer::purgeCache() std::string LLAppViewer::getSecondLifeTitle() const { - return LLTrans::getString("SECOND_LIFE_VIEWER"); + return LLTrans::getString("APP_NAME"); } std::string LLAppViewer::getWindowTitle() const @@ -3256,6 +3275,15 @@ public: } }; +static LLFastTimer::DeclareTimer FTM_AUDIO_UPDATE("Update Audio"); +static LLFastTimer::DeclareTimer FTM_CLEANUP("Cleanup"); +static LLFastTimer::DeclareTimer FTM_IDLE_CB("Idle Callbacks"); +static LLFastTimer::DeclareTimer FTM_LOD_UPDATE("Update LOD"); +static LLFastTimer::DeclareTimer FTM_OBJECTLIST_UPDATE("Update Objectlist"); +static LLFastTimer::DeclareTimer FTM_REGION_UPDATE("Update Region"); +static LLFastTimer::DeclareTimer FTM_WORLD_UPDATE("Update World"); +static LLFastTimer::DeclareTimer FTM_NETWORK("Network"); + /////////////////////////////////////////////////////// // idle() // @@ -3322,7 +3350,7 @@ void LLAppViewer::idle() if (!gDisconnected) { - LLFastTimer t(LLFastTimer::FTM_NETWORK); + LLFastTimer t(FTM_NETWORK); // Update spaceserver timeinfo LLWorld::getInstance()->setSpaceTimeUSec(LLWorld::getInstance()->getSpaceTimeUSec() + (U32)(dt_raw * SEC_TO_MICROSEC)); @@ -3403,7 +3431,7 @@ void LLAppViewer::idle() if (!gDisconnected) { - LLFastTimer t(LLFastTimer::FTM_NETWORK); + LLFastTimer t(FTM_NETWORK); //////////////////////////////////////////////// // @@ -3432,7 +3460,7 @@ void LLAppViewer::idle() // { -// LLFastTimer t(LLFastTimer::FTM_IDLE_CB); +// LLFastTimer t(FTM_IDLE_CB); // Do event notifications if necessary. Yes, we may want to move this elsewhere. gEventNotifier.update(); @@ -3467,7 +3495,7 @@ void LLAppViewer::idle() } { - LLFastTimer t(LLFastTimer::FTM_OBJECTLIST_UPDATE); // Actually "object update" + LLFastTimer t(FTM_OBJECTLIST_UPDATE); // Actually "object update" if (!(logoutRequestSent() && hasSavedFinalSnapshot())) { @@ -3482,7 +3510,7 @@ void LLAppViewer::idle() // { - LLFastTimer t(LLFastTimer::FTM_CLEANUP); + LLFastTimer t(FTM_CLEANUP); gObjectList.cleanDeadObjects(); LLDrawable::cleanupDeadDrawables(); } @@ -3514,7 +3542,7 @@ void LLAppViewer::idle() // { - LLFastTimer t(LLFastTimer::FTM_NETWORK); + LLFastTimer t(FTM_NETWORK); gVLManager.unpackData(); } @@ -3526,7 +3554,7 @@ void LLAppViewer::idle() LLWorld::getInstance()->updateVisibilities(); { const F32 max_region_update_time = .001f; // 1ms - LLFastTimer t(LLFastTimer::FTM_REGION_UPDATE); + LLFastTimer t(FTM_REGION_UPDATE); LLWorld::getInstance()->updateRegions(max_region_update_time); } @@ -3573,7 +3601,7 @@ void LLAppViewer::idle() if (!gNoRender) { - LLFastTimer t(LLFastTimer::FTM_WORLD_UPDATE); + LLFastTimer t(FTM_WORLD_UPDATE); gPipeline.updateMove(); LLWorld::getInstance()->updateParticles(); @@ -3594,14 +3622,17 @@ void LLAppViewer::idle() gAgent.updateCamera(); } + // update media focus + LLViewerMediaFocus::getInstance()->update(); + // objects and camera should be in sync, do LOD calculations now { - LLFastTimer t(LLFastTimer::FTM_LOD_UPDATE); + LLFastTimer t(FTM_LOD_UPDATE); gObjectList.updateApparentAngles(gAgent); } { - LLFastTimer t(LLFastTimer::FTM_AUDIO_UPDATE); + LLFastTimer t(FTM_AUDIO_UPDATE); if (gAudiop) { @@ -3752,6 +3783,8 @@ void LLAppViewer::sendLogoutRequest() static F32 CheckMessagesMaxTime = CHECK_MESSAGES_DEFAULT_MAX_TIME; #endif +static LLFastTimer::DeclareTimer FTM_IDLE_NETWORK("Network"); + void LLAppViewer::idleNetwork() { LLMemType mt_in(LLMemType::MTYPE_IDLE_NETWORK); @@ -3764,7 +3797,7 @@ void LLAppViewer::idleNetwork() if (!gSavedSettings.getBOOL("SpeedTest")) { - LLFastTimer t(LLFastTimer::FTM_IDLE_NETWORK); // decode + LLFastTimer t(FTM_IDLE_NETWORK); // decode // deal with any queued name requests and replies. gCacheName->processPending(); diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp index d02e86a557..ed291c16a8 100644 --- a/indra/newview/llappviewerlinux.cpp +++ b/indra/newview/llappviewerlinux.cpp @@ -451,7 +451,7 @@ gboolean viewer_app_api_GoSLURL(ViewerAppAPI *obj, gchar *slurl, gboolean **succ llinfos << "Was asked to go to slurl: " << slurl << llendl; std::string url = slurl; - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; const bool trusted_browser = false; if (LLURLDispatcher::dispatch(url, web, trusted_browser)) { @@ -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/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp index ca005946c9..2b3939d92f 100644 --- a/indra/newview/llappviewermacosx.cpp +++ b/indra/newview/llappviewermacosx.cpp @@ -50,7 +50,7 @@ #include <Carbon/Carbon.h> #include "lldir.h" #include <signal.h> -class LLWebBrowserCtrl; // for LLURLDispatcher +class LLMediaCtrl; // for LLURLDispatcher namespace { @@ -476,7 +476,7 @@ OSErr AEGURLHandler(const AppleEvent *messagein, AppleEvent *reply, long refIn) url.replace(0, prefix.length(), "secondlife:///app/"); } - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; const bool trusted_browser = false; LLURLDispatcher::dispatch(url, web, trusted_browser); } diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index bb3b9087a1..827ae5855b 100644 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -288,7 +288,8 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) LLFloaterInventory* view = LLFloaterInventory::getActiveInventory(); if(view) { - LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); + LLFocusableElement* focus = gFocusMgr.getKeyboardFocus(); + view->getPanel()->setSelection(content["new_inventory_item"].asUUID(), TAKE_FOCUS_NO); if((LLAssetType::AT_TEXTURE == asset_type || LLAssetType::AT_SOUND == asset_type) && LLFilePicker::instance().getFileCount() <= FILE_COUNT_DISPLAY_THRESHOLD) @@ -297,7 +298,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content) } //LLFloaterInventory::dumpSelectionInformation((void*)view); // restore keyboard focus - gFocusMgr.setKeyboardFocus(focus_ctrl); + gFocusMgr.setKeyboardFocus(focus); } } else diff --git a/indra/newview/llaudiosourcevo.h b/indra/newview/llaudiosourcevo.h index e7bb2837a4..4b70f8bc20 100644 --- a/indra/newview/llaudiosourcevo.h +++ b/indra/newview/llaudiosourcevo.h @@ -34,7 +34,7 @@ #ifndef LL_LLAUDIOSOURCEVO_H #define LL_LLAUDIOSOURCEVO_H -#include "audioengine.h" +#include "llaudioengine.h" #include "llviewerobject.h" class LLViewerObject; diff --git a/indra/newview/llavatarlist.cpp b/indra/newview/llavatarlist.cpp index a85f8710c7..f557adf6a6 100644 --- a/indra/newview/llavatarlist.cpp +++ b/indra/newview/llavatarlist.cpp @@ -200,7 +200,7 @@ BOOL LLAvatarList::update(const std::vector<LLUUID>& all_buddies, const std::str // Changed item in place, need to request sort and update columns // because we might have changed data in a column on which the user // has already sorted. JC - sortItems(); + updateSort(); // re-select items selectMultiple(selected_ids); diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp index bfb2d26870..2403e891f6 100644 --- a/indra/newview/llbottomtray.cpp +++ b/indra/newview/llbottomtray.cpp @@ -42,6 +42,7 @@ #include "llsplitbutton.h" #include "llfloatercamera.h" #include "llimpanel.h" +#include "llactiveimwindow.h" LLBottomTray::LLBottomTray(const LLSD&) : mChicletPanel(NULL), @@ -75,6 +76,8 @@ LLBottomTray::LLBottomTray(const LLSD&) // Necessary for focus movement among child controls setFocusRoot(TRUE); + + LLActiveIMWindow::init(mIMWell); } BOOL LLBottomTray::postBuild() @@ -104,8 +107,10 @@ void LLBottomTray::onChicletClick(LLUICtrl* ctrl) // Show after comm window so it is frontmost (and hence will not // auto-hide) - LLIMFloater::show(chiclet->getSessionId()); - chiclet->setCounter(0); + +// this logic has been moved to LLIMChiclet::handleMouseDown +// LLIMFloater::show(chiclet->getSessionId()); +// chiclet->setCounter(0); } } @@ -130,6 +135,7 @@ void LLBottomTray::sessionAdded(const LLUUID& session_id, const std::string& nam chiclet->setOtherParticipantId(other_participant_id); } } + updateImChicletCount(); } //virtual @@ -139,6 +145,7 @@ void LLBottomTray::sessionRemoved(const LLUUID& session_id) { getChicletPanel()->removeChiclet(session_id); } + updateImChicletCount(); } //virtual @@ -157,7 +164,7 @@ void LLBottomTray::setVisible(BOOL visible) { LLPanel::setVisible(visible); - // *NOTE: we must check mToolbarStack against NULL because sewtVisible is called from the + // *NOTE: we must check mToolbarStack against NULL because setVisible is called from the // LLPanel::initFromParams BEFORE postBuild is called and child controls are not exist yet if (NULL != mToolbarStack) { @@ -177,3 +184,8 @@ void LLBottomTray::setVisible(BOOL visible) } } } + +void LLBottomTray::updateImChicletCount() { + U32 chicletCount = mChicletPanel->getChicletCount(); + mIMWell->setCounter(chicletCount); +} diff --git a/indra/newview/llbottomtray.h b/indra/newview/llbottomtray.h index fec533f9f3..7f606339bc 100644 --- a/indra/newview/llbottomtray.h +++ b/indra/newview/llbottomtray.h @@ -68,6 +68,9 @@ public: virtual void onFocusLost(); virtual void setVisible(BOOL visible); +private: + void updateImChicletCount(); + protected: LLBottomTray(const LLSD& key = LLSD()); diff --git a/indra/newview/llchannelmanager.cpp b/indra/newview/llchannelmanager.cpp index 91945038aa..a8373491cf 100644 --- a/indra/newview/llchannelmanager.cpp +++ b/indra/newview/llchannelmanager.cpp @@ -36,6 +36,7 @@ #include "llappviewer.h" #include "llviewercontrol.h" +#include "llimview.h" #include <algorithm> @@ -62,39 +63,69 @@ void LLChannelManager::onLoginCompleted() for(std::vector<ChannelElem>::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) { - away_notifications +=(*it).channel->getNumberOfHiddenToasts(); + if((*it).channel->getChannelID() == LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))) + { + continue; + } + + if(!(*it).channel->getDisplayToastsAlways()) + { + away_notifications +=(*it).channel->getNumberOfHiddenToasts(); + } } + // *TODO: calculate IM notifications + away_notifications += gIMMgr->getNumberOfUnreadIM(); + if(!away_notifications) { - LLScreenChannel::setStartUpToastShown(); + onStartUpToastClose(); return; } LLChannelManager::Params p; - p.id = LLUUID(STARTUP_CHANNEL_ID); + p.id = LLUUID(gSavedSettings.getString("StartUpChannelUUID")); p.channel_right_bound = getRootView()->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); mStartUpChannel = createChannel(p); if(!mStartUpChannel) + { + onStartUpToastClose(); return; + } - static_cast<LLUICtrl*>(mStartUpChannel)->setCommitCallback(boost::bind(&LLChannelManager::removeStartUpChannel, this)); + mStartUpChannel->setShowToasts(true); + static_cast<LLUICtrl*>(mStartUpChannel)->setCommitCallback(boost::bind(&LLChannelManager::onStartUpToastClose, this)); mStartUpChannel->createStartUpToast(away_notifications, gSavedSettings.getS32("ChannelBottomPanelMargin"), gSavedSettings.getS32("StartUpToastTime")); } //-------------------------------------------------------------------------- -void LLChannelManager::removeStartUpChannel() +void LLChannelManager::onStartUpToastClose() { - if(!mStartUpChannel) - return; + if(mStartUpChannel) + { + mStartUpChannel->setVisible(FALSE); + mStartUpChannel->closeStartUpToast(); + getRootView()->removeChild(mStartUpChannel); + removeChannelByID(LLUUID(gSavedSettings.getString("StartUpChannelUUID"))); + delete mStartUpChannel; + mStartUpChannel = NULL; + } - mStartUpChannel->setVisible(FALSE); - mStartUpChannel->closeStartUpToast(); - getRootView()->removeChild(mStartUpChannel); - delete mStartUpChannel; - mStartUpChannel = NULL; + // set StartUp Toast Flag + LLScreenChannel::setStartUpToastShown(); + + // allow all other channels to show incoming toasts + for(std::vector<ChannelElem>::iterator it = mChannelList.begin(); it != mChannelList.end(); ++it) + { + (*it).channel->setShowToasts(true); + } + + // force NEARBY CHAT CHANNEL to repost all toasts if present + LLScreenChannel* nearby_channel = getChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); + nearby_channel->loadStoredToastsToChannel(); + nearby_channel->setCanStoreToasts(false); } //-------------------------------------------------------------------------- @@ -114,10 +145,11 @@ LLScreenChannel* LLChannelManager::createChannel(LLChannelManager::Params& p) if(new_channel) return new_channel; - new_channel = new LLScreenChannel(); + new_channel = new LLScreenChannel(p.id); getRootView()->addChild(new_channel); new_channel->init(p.channel_right_bound - p.channel_width, p.channel_right_bound); new_channel->setToastAlignment(p.align); + new_channel->setDisplayToastsAlways(p.display_toasts_always); ChannelElem new_elem; new_elem.id = p.id; @@ -170,15 +202,26 @@ void LLChannelManager::reshape(S32 width, S32 height, BOOL called_from_parent) } //-------------------------------------------------------------------------- - -LLScreenChannel* LLChannelManager::getStartUpChannel() +void LLChannelManager::removeChannelByID(const LLUUID id) { - return mStartUpChannel; + std::vector<ChannelElem>::iterator it = find(mChannelList.begin(), mChannelList.end(), id); + if(it != mChannelList.end()) + { + mChannelList.erase(it); + } } //-------------------------------------------------------------------------- +void LLChannelManager::removeChannelByChiclet(const LLChiclet* chiclet) +{ + std::vector<ChannelElem>::iterator it = find(mChannelList.begin(), mChannelList.end(), chiclet); + if(it != mChannelList.end()) + { + mChannelList.erase(it); + } +} - +//-------------------------------------------------------------------------- diff --git a/indra/newview/llchannelmanager.h b/indra/newview/llchannelmanager.h index 6adc79713a..e26c96b62e 100644 --- a/indra/newview/llchannelmanager.h +++ b/indra/newview/llchannelmanager.h @@ -44,9 +44,6 @@ namespace LLNotificationsUI { - -#define STARTUP_CHANNEL_ID "AEED3193-8709-4693-8558-7452CCA97AE5" - /** * Manager for screen channels. * Responsible for instantiating and retrieving screen channels. @@ -56,17 +53,16 @@ class LLChannelManager : public LLUICtrl, public LLSingleton<LLChannelManager> public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { - Optional<LLUUID> id; - Optional<LLChiclet*> chiclet; - Optional<S32> channel_right_bound; - Optional<S32> channel_width; - Optional<EToastAlignment> align; - - Params(): id("id", LLUUID("")), - chiclet("chiclet", NULL), - channel_right_bound("channel_right_bound", 0), - channel_width("channel_width", 0), - align("align", NA_BOTTOM) + LLUUID id; + LLChiclet* chiclet; + S32 channel_right_bound; + S32 channel_width; + bool display_toasts_always; + EToastAlignment align; + + Params(): id(LLUUID("")), chiclet(NULL), + channel_right_bound(0), channel_width(0), + display_toasts_always(false), align(NA_BOTTOM) {} }; @@ -102,7 +98,8 @@ public: // On LoginCompleted - show StartUp toast void onLoginCompleted(); - void removeStartUpChannel(); + // removes a channel intended for the startup toast and allows other channels to show their toasts + void onStartUpToastClose(); //TODO: make protected? in order to be shure that channels are created only by notification handlers LLScreenChannel* createChannel(LLChannelManager::Params& p); @@ -110,9 +107,11 @@ public: LLScreenChannel* getChannelByID(const LLUUID id); LLScreenChannel* getChannelByChiclet(const LLChiclet* chiclet); - void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); + // remove channel methods + void removeChannelByID(const LLUUID id); + void removeChannelByChiclet(const LLChiclet* chiclet); - LLScreenChannel* getStartUpChannel(); + void reshape(S32 width, S32 height, BOOL called_from_parent = TRUE); private: diff --git a/indra/newview/llchatbar.cpp b/indra/newview/llchatbar.cpp index 7a118deb8a..3ee2c93961 100644 --- a/indra/newview/llchatbar.cpp +++ b/indra/newview/llchatbar.cpp @@ -103,10 +103,6 @@ LLChatBar::LLChatBar() mObserver(NULL) { setIsChrome(TRUE); - -#if !LL_RELEASE_FOR_DOWNLOAD - childDisplayNotFound(); -#endif } @@ -125,7 +121,7 @@ BOOL LLChatBar::postBuild() // * NOTE: mantipov: getChild with default parameters returns dummy widget. // Seems this class will be completle removed // attempt to bind to an existing combo box named gesture - setGestureCombo(getChild<LLComboBox>( "Gesture", TRUE, FALSE)); + setGestureCombo(findChild<LLComboBox>( "Gesture")); mInputEditor = getChild<LLLineEditor>("Chat Editor"); mInputEditor->setKeystrokeCallback(&onInputEditorKeystroke, this); @@ -682,7 +678,7 @@ public: // Your code here bool handle(const LLSD& tokens, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { if (tokens.size() < 2) return false; S32 channel = tokens[0].asInteger(); diff --git a/indra/newview/llchatitemscontainerctrl.cpp b/indra/newview/llchatitemscontainerctrl.cpp index a63477a442..05c574b154 100644 --- a/indra/newview/llchatitemscontainerctrl.cpp +++ b/indra/newview/llchatitemscontainerctrl.cpp @@ -93,13 +93,11 @@ void LLChatItemCtrl::reshape (S32 width, S32 height, BOOL called_from_parent ) caption->reshape( width - 4, caption_rect.getHeight(), 1); caption->setRect(caption_rect); - LLRect msg_text_rect = msg_text->getRect(); msg_text_rect.setLeftTopAndSize( msg_left_offset, height - caption_rect.getHeight() , width - msg_left_offset - msg_right_offset, height - caption_rect.getHeight()); msg_text->reshape( width - msg_left_offset - msg_right_offset, height - caption_rect.getHeight(), 1); msg_text->setRect(msg_text_rect); } - } BOOL LLChatItemCtrl::postBuild() @@ -246,7 +244,9 @@ void LLChatItemCtrl::setHeaderVisibility(EShowItemHeader e) bool LLChatItemCtrl::canAddText () { - LLChatMsgBox* msg_text = getChild<LLChatMsgBox>("msg_text", false); + LLChatMsgBox* msg_text = findChild<LLChatMsgBox>("msg_text"); + if(!msg_text) + return false; return msg_text->getTextLinesNum()<10; } diff --git a/indra/newview/llchatmsgbox.cpp b/indra/newview/llchatmsgbox.cpp index 933d9b8771..9399e1f68d 100644 --- a/indra/newview/llchatmsgbox.cpp +++ b/indra/newview/llchatmsgbox.cpp @@ -371,7 +371,7 @@ void LLChatMsgBox::drawText( S32 x, S32 y, const LLColor4& color ) mHAlign, mVAlign, mFontStyle, mShadowType, - line_length, getRect().getWidth(), NULL, TRUE, mUseEllipses ); + line_length, getRect().getWidth(), NULL, mUseEllipses ); cur_pos += line_length + 1; y -= llfloor(mFontGL->getLineHeight()) + mLineSpacing; diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp index 80e27bd366..6b4dfa73a4 100644 --- a/indra/newview/llchiclet.cpp +++ b/indra/newview/llchiclet.cpp @@ -45,6 +45,7 @@ #include "lltextbox.h" #include "llvoiceclient.h" #include "llvoicecontrolpanel.h" +#include "llgroupmgr.h" static const std::string P2P_MENU_NAME = "IMChiclet P2P Menu"; static const std::string GROUP_MENU_NAME = "IMChiclet Group Menu"; @@ -54,6 +55,13 @@ static LLDefaultChildRegistry::Register<LLTalkButton> t2("chiclet_talk"); static LLDefaultChildRegistry::Register<LLNotificationChiclet> t3("chiclet_notification"); static LLDefaultChildRegistry::Register<LLIMChiclet> t4("chiclet_im"); +S32 LLNotificationChiclet::mUreadSystemNotifications = 0; +S32 LLNotificationChiclet::mUreadIMNotifications = 0; + + +boost::signals2::signal<LLChiclet* (const LLUUID&), + LLIMChiclet::CollectChicletCombiner<std::list<LLChiclet*> > > + LLIMChiclet::sFindChicletsSignal; ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -111,6 +119,12 @@ boost::signals2::connection LLNotificationChiclet::setClickCallback( return mButton->setClickedCallback(cb); } +void LLNotificationChiclet::updateUreadIMNotifications() +{ + mUreadIMNotifications = gIMMgr->getNumberOfUnreadIM(); + setCounter(mUreadSystemNotifications + mUreadIMNotifications); +} + ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// @@ -175,6 +189,7 @@ void LLChiclet::setValue(const LLSD& value) LLIMChiclet::Params::Params() : avatar_icon("avatar_icon") +, group_insignia("group_insignia") , unread_notifications("unread_notifications") , speaker("speaker") , show_speaker("show_speaker") @@ -182,8 +197,14 @@ LLIMChiclet::Params::Params() rect(LLRect(0, 25, 45, 0)); avatar_icon.name("avatar_icon"); + avatar_icon.visible(false); avatar_icon.rect(LLRect(0, 25, 25, 0)); + //it's an icon for a group in case there is a group chat created + group_insignia.name("group_icon"); + group_insignia.visible(false); + group_insignia.rect(LLRect(0, 25, 25, 0)); + unread_notifications.name("unread"); unread_notifications.rect(LLRect(25, 25, 45, 0)); unread_notifications.font(LLFontGL::getFontSansSerif()); @@ -199,7 +220,9 @@ LLIMChiclet::Params::Params() LLIMChiclet::LLIMChiclet(const Params& p) : LLChiclet(p) +, LLGroupMgrObserver(LLUUID()) , mAvatarCtrl(NULL) +, mGroupInsignia(NULL) , mCounterCtrl(NULL) , mSpeakerCtrl(NULL) , mShowSpeaker(p.show_speaker) @@ -209,6 +232,12 @@ LLIMChiclet::LLIMChiclet(const Params& p) mAvatarCtrl = LLUICtrlFactory::create<LLChicletAvatarIconCtrl>(avatar_params); addChild(mAvatarCtrl); + //Before setOtherParticipantId() we are UNAWARE which dialog type will it be + //so keeping both icons for all both p2p and group chat cases + LLIconCtrl::Params grop_icon_params = p.group_insignia; + mGroupInsignia = LLUICtrlFactory::create<LLIconCtrl>(grop_icon_params); + addChild(mGroupInsignia); + LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications; mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params); addChild(mCounterCtrl); @@ -225,7 +254,7 @@ LLIMChiclet::LLIMChiclet(const Params& p) LLIMChiclet::~LLIMChiclet() { - + LLGroupMgr::getInstance()->removeObserver(this); } @@ -250,6 +279,12 @@ void LLIMChiclet::setCounter(S32 counter) } } +void LLIMChiclet::onMouseDown() +{ + LLIMFloater::toggle(getSessionId()); + setCounter(0); +} + LLRect LLIMChiclet::getRequiredRect() { LLRect rect(0, 0, mAvatarCtrl->getRect().getWidth(), 0); @@ -277,19 +312,84 @@ void LLIMChiclet::setShowCounter(bool show) } } + +void LLIMChiclet::setSessionId(const LLUUID& session_id) +{ + LLChiclet::setSessionId(session_id); + + //for a group chat session_id = group_id + LLFloaterIMPanel* im = LLIMMgr::getInstance()->findFloaterBySession(session_id); + if (!im) return; //should never happen + + EInstantMessage type = im->getDialogType(); + if (type == IM_SESSION_INVITE || type == IM_SESSION_GROUP_START) + { + if (!gAgent.isInGroup(session_id)) return; + + if (mGroupInsignia) { + LLGroupMgr* grp_mgr = LLGroupMgr::getInstance(); + LLGroupMgrGroupData* group_data = grp_mgr->getGroupData(session_id); + if (group_data && group_data->mInsigniaID.notNull()) + { + mGroupInsignia->setVisible(TRUE); + mGroupInsignia->setValue(group_data->mInsigniaID); + } + else + { + mID = session_id; //needed for LLGroupMgrObserver + grp_mgr->addObserver(this); + grp_mgr->sendGroupPropertiesRequest(session_id); + } + } + } +} + void LLIMChiclet::setIMSessionName(const std::string& name) { setToolTip(name); } +//session id should be set before calling this void LLIMChiclet::setOtherParticipantId(const LLUUID& other_participant_id) { - if (mAvatarCtrl) + llassert(getSessionId().notNull()); + + LLFloaterIMPanel*floater = gIMMgr->findFloaterBySession(getSessionId()); + + //all alive sessions have alive floater, haven't they? + llassert(floater); + + mOtherParticipantId = other_participant_id; + + if (mAvatarCtrl && floater->getDialogType() == IM_NOTHING_SPECIAL) { + mAvatarCtrl->setVisible(TRUE); mAvatarCtrl->setValue(other_participant_id); } } + +void LLIMChiclet::changed(LLGroupChange gc) +{ + LLSD group_insignia = mGroupInsignia->getValue(); + if (group_insignia.isUUID() && group_insignia.asUUID().notNull()) return; + + if (GC_PROPERTIES == gc) + { + LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(getSessionId()); + if (group_data && group_data->mInsigniaID.notNull()) + { + mGroupInsignia->setVisible(TRUE); + mGroupInsignia->setValue(group_data->mInsigniaID); + } + } +} + +LLUUID LLIMChiclet::getOtherParticipantId() +{ + return mOtherParticipantId; +} + void LLIMChiclet::updateMenuItems() { if(!mPopupMenu) @@ -299,13 +399,19 @@ void LLIMChiclet::updateMenuItems() if(P2P_MENU_NAME == mPopupMenu->getName()) { - bool is_friend = LLAvatarActions::isFriend(mAvatarCtrl->getAvatarId()); + bool is_friend = LLAvatarActions::isFriend(getOtherParticipantId()); mPopupMenu->getChild<LLUICtrl>("Add Friend")->setEnabled(!is_friend); mPopupMenu->getChild<LLUICtrl>("Remove Friend")->setEnabled(is_friend); } } +BOOL LLIMChiclet::handleMouseDown(S32 x, S32 y, MASK mask) +{ + onMouseDown(); + return LLChiclet::handleMouseDown(x, y, mask); +} + void LLIMChiclet::setShowSpeaker(bool show) { bool needs_resize = getShowSpeaker() != show; @@ -391,7 +497,7 @@ void LLIMChiclet::createPopupMenu() void LLIMChiclet::onMenuItemClicked(const LLSD& user_data) { std::string level = user_data.asString(); - LLUUID other_participant_id = mAvatarCtrl->getAvatarId(); + LLUUID other_participant_id = getOtherParticipantId(); if("profile" == level) { @@ -405,10 +511,6 @@ void LLIMChiclet::onMenuItemClicked(const LLSD& user_data) { LLAvatarActions::requestFriendshipDialog(other_participant_id); } - else if("remove" == level) - { - LLAvatarActions::removeFriendDialog(other_participant_id); - } else if("group chat" == level) { LLGroupActions::startChat(other_participant_id); @@ -491,16 +593,20 @@ LLChicletPanel::~LLChicletPanel() void im_chiclet_callback(LLChicletPanel* panel, const LLSD& data){ LLUUID session_id = data["session_id"].asUUID(); - LLChiclet* chiclet = panel->findChiclet<LLChiclet>(session_id); - - if (chiclet) - { - chiclet->setCounter(data["num_unread"].asInteger()); + std::list<LLChiclet*> chiclets = LLIMChiclet::sFindChicletsSignal(session_id); + std::list<LLChiclet *>::iterator iter; + for (iter = chiclets.begin(); iter != chiclets.end(); iter++) { + LLChiclet* chiclet = *iter; + if (chiclet != NULL) + { + chiclet->setCounter(data["num_unread"].asInteger()); + } + else + { + llwarns << "Unable to set counter for chiclet " << session_id << llendl; + } } - else - { - llwarns << "Unable to set counter for chiclet " << session_id << llendl; - } + } @@ -508,6 +614,7 @@ BOOL LLChicletPanel::postBuild() { LLPanel::postBuild(); LLIMModel::instance().addChangedCallback(boost::bind(im_chiclet_callback, this, _1)); + LLIMChiclet::sFindChicletsSignal.connect(boost::bind(&LLChicletPanel::findChiclet<LLChiclet>, this, _1)); return TRUE; } diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h index 103443ccd8..b1e73c9d8d 100644 --- a/indra/newview/llchiclet.h +++ b/indra/newview/llchiclet.h @@ -38,6 +38,7 @@ #include "llpanel.h" #include "lltextbox.h" #include "lloutputmonitorctrl.h" +#include "llgroupmgr.h" class LLVoiceControlPanel; class LLMenuGL; @@ -235,13 +236,15 @@ private: * IMChiclet displays avatar's icon, number of unread messages(optional) * and voice chat status(optional). */ -class LLIMChiclet : public LLChiclet +class LLIMChiclet : public LLChiclet, LLGroupMgrObserver { public: struct Params : public LLInitParam::Block<Params, LLChiclet::Params> { Optional<LLChicletAvatarIconCtrl::Params> avatar_icon; + Optional<LLIconCtrl::Params> group_insignia; + Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications; Optional<LLChicletSpeakerCtrl::Params> speaker; @@ -253,6 +256,8 @@ public: /*virtual*/ ~LLIMChiclet(); + virtual void setSessionId(const LLUUID& session_id); + /* * Sets IM session name. This name will be displayed in chiclet tooltip. */ @@ -260,10 +265,16 @@ public: /* * Sets id of person/group user is chatting with. + * Session id should be set before calling this */ virtual void setOtherParticipantId(const LLUUID& other_participant_id); /* + * Gets id of person/group user is chatting with. + */ + virtual LLUUID getOtherParticipantId(); + + /* * Shows/hides voice chat status control. */ virtual void setShowSpeaker(bool show); @@ -294,12 +305,23 @@ public: */ /*virtual*/ void draw(); + /** + * The action taken on mouse down event. + * + * Made public so that it can be triggered from outside + * (more specifically, from the Active IM window). + */ + void onMouseDown(); + /* * Returns rect, required to display chiclet. * Width is the only valid value. */ /*virtual*/ LLRect getRequiredRect(); + /** comes from LLGroupMgrObserver */ + virtual void changed(LLGroupChange gc); + protected: LLIMChiclet(const Params& p); @@ -326,14 +348,43 @@ protected: */ /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask); + protected: LLChicletAvatarIconCtrl* mAvatarCtrl; + + /** the icon of a group in case of group chat */ + LLIconCtrl* mGroupInsignia; LLChicletNotificationCounterCtrl* mCounterCtrl; LLChicletSpeakerCtrl* mSpeakerCtrl; LLMenuGL* mPopupMenu; bool mShowSpeaker; + + /** the id of another participant, either an avatar id or a group id*/ + LLUUID mOtherParticipantId; + + template<typename Container> + struct CollectChicletCombiner { + typedef Container result_type; + + template<typename InputIterator> + Container operator()(InputIterator first, InputIterator last) const { + Container c = Container(); + for (InputIterator iter = first; iter != last; iter++) { + if (*iter != NULL) { + c.push_back(*iter); + } + } + return c; + } + }; + +public: + static boost::signals2::signal<LLChiclet* (const LLUUID&), + CollectChicletCombiner<std::list<LLChiclet*> > > + sFindChicletsSignal; }; /* @@ -366,12 +417,20 @@ public: // Notification Chiclet Window void setNotificationChicletWindow(LLFloater* wnd) { mNotificationChicletWindow = wnd; } + // methods for updating a number of unread System or IM notifications + void incUreadSystemNotifications() { setCounter(++mUreadSystemNotifications + mUreadIMNotifications); } + void decUreadSystemNotifications() { setCounter(--mUreadSystemNotifications + mUreadIMNotifications); } + void updateUreadIMNotifications(); + protected: LLNotificationChiclet(const Params& p); friend class LLUICtrlFactory; LLFloater* mNotificationChicletWindow; + static S32 mUreadSystemNotifications; + static S32 mUreadIMNotifications; + protected: LLButton* mButton; LLChicletNotificationCounterCtrl* mCounterCtrl; diff --git a/indra/newview/llclassifiedstatsresponder.cpp b/indra/newview/llclassifiedstatsresponder.cpp index a218e2b4e5..ecd1879090 100644 --- a/indra/newview/llclassifiedstatsresponder.cpp +++ b/indra/newview/llclassifiedstatsresponder.cpp @@ -33,7 +33,6 @@ #include "llviewerprecompiledheaders.h" -#include "llagent.h" #include "llclassifiedstatsresponder.h" #include "llpanelclassified.h" diff --git a/indra/newview/llcommandhandler.cpp b/indra/newview/llcommandhandler.cpp index 422c94ade5..a04182a910 100644 --- a/indra/newview/llcommandhandler.cpp +++ b/indra/newview/llcommandhandler.cpp @@ -55,7 +55,7 @@ public: bool dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser); private: @@ -84,7 +84,7 @@ void LLCommandHandlerRegistry::add(const char* cmd, bool require_trusted_browser bool LLCommandHandlerRegistry::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser) { std::map<std::string, LLCommandHandlerInfo>::iterator it = mMap.find(cmd); @@ -126,7 +126,7 @@ LLCommandHandler::~LLCommandHandler() bool LLCommandDispatcher::dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser) { return LLCommandHandlerRegistry::instance().dispatch( diff --git a/indra/newview/llcommandhandler.h b/indra/newview/llcommandhandler.h index ab4c2cc488..5cb3ee73d4 100644 --- a/indra/newview/llcommandhandler.h +++ b/indra/newview/llcommandhandler.h @@ -47,7 +47,7 @@ public: // Your code here bool handle(const LLSD& tokens, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { if (tokens.size() < 1) return false; LLUUID id( tokens[0] ); @@ -60,7 +60,7 @@ LLFooHandler gFooHandler; */ -class LLWebBrowserCtrl; +class LLMediaCtrl; class LLCommandHandler { @@ -68,14 +68,14 @@ public: LLCommandHandler(const char* command, bool allow_from_untrusted_browser); // Automatically registers object to get called when // command is executed. All commands can be processed - // in links from LLWebBrowserCtrl, but some (like teleport) + // in links from LLMediaCtrl, but some (like teleport) // should not be allowed from outside the app. virtual ~LLCommandHandler(); virtual bool handle(const LLSD& params, const LLSD& query_map, - LLWebBrowserCtrl* web) = 0; + LLMediaCtrl* web) = 0; // For URL secondlife:///app/foo/bar/baz?cat=1&dog=2 // @params - array of "bar", "baz", possibly empty // @query_map - map of "cat" -> 1, "dog" -> 2, possibly empty @@ -91,7 +91,7 @@ public: static bool dispatch(const std::string& cmd, const LLSD& params, const LLSD& query_map, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser); // Execute a command registered via the above mechanism, // passing string parameters. diff --git a/indra/newview/lldrawable.cpp b/indra/newview/lldrawable.cpp index d8bd32382f..7cc78aff92 100644 --- a/indra/newview/lldrawable.cpp +++ b/indra/newview/lldrawable.cpp @@ -60,6 +60,9 @@ const F32 MAX_INTERPOLATE_DISTANCE_SQUARED = 10.f * 10.f; const F32 OBJECT_DAMPING_TIME_CONSTANT = 0.06f; const F32 MIN_SHADOW_CASTER_RADIUS = 2.0f; +static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound"); + + //////////////////////// // // Inline implementations. @@ -188,7 +191,7 @@ BOOL LLDrawable::isLight() const void LLDrawable::cleanupReferences() { - LLFastTimer t(LLFastTimer::FTM_PIPELINE); + LLFastTimer t(FTM_PIPELINE); std::for_each(mFaces.begin(), mFaces.end(), DeletePointer()); mFaces.clear(); @@ -1033,7 +1036,7 @@ void LLSpatialBridge::updateSpatialExtents() LLSpatialGroup* root = (LLSpatialGroup*) mOctree->getListener(0); { - LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND); + LLFastTimer ftm(FTM_CULL_REBOUND); root->rebound(); } diff --git a/indra/newview/lldrawable.h b/indra/newview/lldrawable.h index 940e1fc968..c765980c30 100644 --- a/indra/newview/lldrawable.h +++ b/indra/newview/lldrawable.h @@ -63,7 +63,6 @@ class LLViewerTexture; // Can have multiple silhouettes for each object const U32 SILHOUETTE_HIGHLIGHT = 0; - // All data for new renderer goes into this class. class LLDrawable : public LLRefCount { diff --git a/indra/newview/lldrawpoolalpha.cpp b/indra/newview/lldrawpoolalpha.cpp index 267f83f295..88f79fc1b9 100644 --- a/indra/newview/lldrawpoolalpha.cpp +++ b/indra/newview/lldrawpoolalpha.cpp @@ -42,7 +42,6 @@ #include "llcubemap.h" #include "llsky.h" -#include "llagent.h" #include "lldrawable.h" #include "llface.h" #include "llviewercamera.h" @@ -90,7 +89,7 @@ void LLDrawPoolAlpha::endDeferredPass(S32 pass) { gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.4f); { - LLFastTimer t(LLFastTimer::FTM_RENDER_GRASS); + LLFastTimer t(FTM_RENDER_GRASS); gDeferredTreeProgram.bind(); LLGLEnable test(GL_ALPHA_TEST); //render alpha masked objects @@ -112,7 +111,7 @@ S32 LLDrawPoolAlpha::getNumPostDeferredPasses() void LLDrawPoolAlpha::beginPostDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA); + LLFastTimer t(FTM_RENDER_ALPHA); simple_shader = &gDeferredAlphaProgram; fullbright_shader = &gDeferredFullbrightProgram; @@ -139,7 +138,7 @@ void LLDrawPoolAlpha::renderPostDeferred(S32 pass) void LLDrawPoolAlpha::beginRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA); + LLFastTimer t(FTM_RENDER_ALPHA); if (LLPipeline::sUnderWaterRender) { @@ -163,7 +162,7 @@ void LLDrawPoolAlpha::beginRenderPass(S32 pass) void LLDrawPoolAlpha::endRenderPass( S32 pass ) { - LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA); + LLFastTimer t(FTM_RENDER_ALPHA); LLRenderPass::endRenderPass(pass); if(gPipeline.canUseWindLightShaders()) @@ -174,7 +173,7 @@ void LLDrawPoolAlpha::endRenderPass( S32 pass ) void LLDrawPoolAlpha::render(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_ALPHA); + LLFastTimer t(FTM_RENDER_ALPHA); LLGLSPipelineAlpha gls_pipeline_alpha; diff --git a/indra/newview/lldrawpoolavatar.cpp b/indra/newview/lldrawpoolavatar.cpp index b15cd0b0dc..565f906a3b 100644 --- a/indra/newview/lldrawpoolavatar.cpp +++ b/indra/newview/lldrawpoolavatar.cpp @@ -38,7 +38,6 @@ #include "llvoavatar.h" #include "m3math.h" -#include "llagent.h" #include "lldrawable.h" #include "llface.h" #include "llsky.h" @@ -89,12 +88,13 @@ S32 AVATAR_OFFSET_TEX0 = 32; S32 AVATAR_OFFSET_TEX1 = 40; S32 AVATAR_VERTEX_BYTES = 48; - BOOL gAvatarEmbossBumpMap = FALSE; static BOOL sRenderingSkinned = FALSE; S32 normal_channel = -1; S32 specular_channel = -1; +static LLFastTimer::DeclareTimer FTM_SHADOW_AVATAR("Avatar Shadow"); + LLDrawPoolAvatar::LLDrawPoolAvatar() : LLFacePool(POOL_AVATAR) { @@ -154,7 +154,7 @@ S32 LLDrawPoolAvatar::getNumDeferredPasses() void LLDrawPoolAvatar::beginDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS); + LLFastTimer t(FTM_RENDER_CHARACTERS); if (LLPipeline::sImpostorRender) { @@ -178,7 +178,7 @@ void LLDrawPoolAvatar::beginDeferredPass(S32 pass) void LLDrawPoolAvatar::endDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS); + LLFastTimer t(FTM_RENDER_CHARACTERS); if (LLPipeline::sImpostorRender) { @@ -248,7 +248,7 @@ S32 LLDrawPoolAvatar::getNumShadowPasses() void LLDrawPoolAvatar::beginShadowPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_AVATAR); + LLFastTimer t(FTM_SHADOW_AVATAR); sVertexProgram = &gDeferredAvatarShadowProgram; if (sShaderLevel > 0) @@ -270,7 +270,7 @@ void LLDrawPoolAvatar::beginShadowPass(S32 pass) void LLDrawPoolAvatar::endShadowPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_AVATAR); + LLFastTimer t(FTM_SHADOW_AVATAR); if (sShaderLevel > 0) { @@ -284,7 +284,7 @@ void LLDrawPoolAvatar::endShadowPass(S32 pass) void LLDrawPoolAvatar::renderShadow(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_AVATAR); + LLFastTimer t(FTM_SHADOW_AVATAR); if (mDrawFace.empty()) { @@ -320,7 +320,7 @@ S32 LLDrawPoolAvatar::getNumPasses() void LLDrawPoolAvatar::render(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS); + LLFastTimer t(FTM_RENDER_CHARACTERS); if (LLPipeline::sImpostorRender) { renderAvatars(NULL, 2); @@ -332,7 +332,7 @@ void LLDrawPoolAvatar::render(S32 pass) void LLDrawPoolAvatar::beginRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS); + LLFastTimer t(FTM_RENDER_CHARACTERS); //reset vertex buffer mappings LLVertexBuffer::unbind(); @@ -358,7 +358,7 @@ void LLDrawPoolAvatar::beginRenderPass(S32 pass) void LLDrawPoolAvatar::endRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_CHARACTERS); + LLFastTimer t(FTM_RENDER_CHARACTERS); if (LLPipeline::sImpostorRender) { diff --git a/indra/newview/lldrawpoolbump.cpp b/indra/newview/lldrawpoolbump.cpp index 971949e885..331ba67d36 100644 --- a/indra/newview/lldrawpoolbump.cpp +++ b/indra/newview/lldrawpoolbump.cpp @@ -43,7 +43,6 @@ #include "llglheaders.h" #include "llrender.h" -#include "llagent.h" #include "llcubemap.h" #include "lldrawable.h" #include "llface.h" @@ -74,6 +73,7 @@ const U32 VERTEX_MASK_BUMP = LLVertexBuffer::MAP_VERTEX |LLVertexBuffer::MAP_TEX U32 LLDrawPoolBump::sVertexMask = VERTEX_MASK_SHINY; + static LLGLSLShader* shader = NULL; static S32 cube_channel = -1; static S32 diffuse_channel = -1; @@ -220,7 +220,7 @@ S32 LLDrawPoolBump::getNumPasses() void LLDrawPoolBump::beginRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer t(FTM_RENDER_BUMP); switch( pass ) { case 0: @@ -247,7 +247,7 @@ void LLDrawPoolBump::beginRenderPass(S32 pass) void LLDrawPoolBump::render(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer t(FTM_RENDER_BUMP); if (!gPipeline.hasRenderType(LLDrawPool::POOL_SIMPLE)) { @@ -280,7 +280,7 @@ void LLDrawPoolBump::render(S32 pass) void LLDrawPoolBump::endRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer t(FTM_RENDER_BUMP); switch( pass ) { case 0: @@ -308,7 +308,7 @@ void LLDrawPoolBump::endRenderPass(S32 pass) //static void LLDrawPoolBump::beginShiny(bool invisible) { - LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); + LLFastTimer t(FTM_RENDER_SHINY); if (!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY)|| invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)) { @@ -383,7 +383,7 @@ void LLDrawPoolBump::beginShiny(bool invisible) void LLDrawPoolBump::renderShiny(bool invisible) { - LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); + LLFastTimer t(FTM_RENDER_SHINY); if (!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY)|| invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)) { @@ -410,7 +410,7 @@ void LLDrawPoolBump::renderShiny(bool invisible) void LLDrawPoolBump::endShiny(bool invisible) { - LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); + LLFastTimer t(FTM_RENDER_SHINY); if (!invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_SHINY)|| invisible && !gPipeline.hasRenderBatches(LLRenderPass::PASS_INVISI_SHINY)) { @@ -450,7 +450,7 @@ void LLDrawPoolBump::endShiny(bool invisible) void LLDrawPoolBump::beginFullbrightShiny() { - LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); + LLFastTimer t(FTM_RENDER_SHINY); if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY)) { return; @@ -499,7 +499,7 @@ void LLDrawPoolBump::beginFullbrightShiny() void LLDrawPoolBump::renderFullbrightShiny() { - LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); + LLFastTimer t(FTM_RENDER_SHINY); if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY)) { return; @@ -514,7 +514,7 @@ void LLDrawPoolBump::renderFullbrightShiny() void LLDrawPoolBump::endFullbrightShiny() { - LLFastTimer t(LLFastTimer::FTM_RENDER_SHINY); + LLFastTimer t(FTM_RENDER_SHINY); if (!gPipeline.hasRenderBatches(LLRenderPass::PASS_FULLBRIGHT_SHINY)) { return; @@ -624,7 +624,7 @@ void LLDrawPoolBump::beginBump() } sVertexMask = VERTEX_MASK_BUMP; - LLFastTimer t(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer t(FTM_RENDER_BUMP); // Optional second pass: emboss bump map stop_glerror(); @@ -668,7 +668,7 @@ void LLDrawPoolBump::renderBump() return; } - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer ftm(FTM_RENDER_BUMP); LLGLDisable fog(GL_FOG); LLGLDepthTest gls_depth(GL_TRUE, GL_FALSE, GL_LEQUAL); LLGLEnable blend(GL_BLEND); @@ -705,7 +705,7 @@ void LLDrawPoolBump::beginDeferredPass(S32 pass) { return; } - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer ftm(FTM_RENDER_BUMP); mShiny = TRUE; gDeferredBumpProgram.bind(); diffuse_channel = gDeferredBumpProgram.enableTexture(LLViewerShaderMgr::DIFFUSE_MAP); @@ -720,7 +720,7 @@ void LLDrawPoolBump::endDeferredPass(S32 pass) { return; } - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer ftm(FTM_RENDER_BUMP); mShiny = FALSE; gDeferredBumpProgram.disableTexture(LLViewerShaderMgr::DIFFUSE_MAP); gDeferredBumpProgram.disableTexture(LLViewerShaderMgr::BUMP_MAP); @@ -734,7 +734,7 @@ void LLDrawPoolBump::renderDeferred(S32 pass) { return; } - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BUMP); + LLFastTimer ftm(FTM_RENDER_BUMP); U32 type = LLRenderPass::PASS_BUMP; LLCullResult::drawinfo_list_t::iterator begin = gPipeline.beginRenderMap(type); @@ -1250,7 +1250,7 @@ void LLDrawPoolBump::pushBatch(LLDrawInfo& params, U32 mask, BOOL texture) void LLDrawPoolInvisible::render(S32 pass) { //render invisiprims - LLFastTimer t(LLFastTimer::FTM_RENDER_INVISIBLE); + LLFastTimer t(FTM_RENDER_INVISIBLE); U32 invisi_mask = LLVertexBuffer::MAP_VERTEX; glStencilMask(0); @@ -1279,7 +1279,7 @@ void LLDrawPoolInvisible::endDeferredPass( S32 pass ) void LLDrawPoolInvisible::renderDeferred( S32 pass ) { //render invisiprims; this doesn't work becaue it also blocks all the post-deferred stuff - LLFastTimer t(LLFastTimer::FTM_RENDER_INVISIBLE); + LLFastTimer t(FTM_RENDER_INVISIBLE); U32 invisi_mask = LLVertexBuffer::MAP_VERTEX; glStencilMask(0); diff --git a/indra/newview/lldrawpoolsimple.cpp b/indra/newview/lldrawpoolsimple.cpp index 1fdf87f6d2..331536fdca 100644 --- a/indra/newview/lldrawpoolsimple.cpp +++ b/indra/newview/lldrawpoolsimple.cpp @@ -35,7 +35,6 @@ #include "lldrawpoolsimple.h" #include "llviewercamera.h" -#include "llagent.h" #include "lldrawable.h" #include "llface.h" #include "llsky.h" @@ -50,7 +49,7 @@ static LLGLSLShader* fullbright_shader = NULL; void LLDrawPoolGlow::render(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_GLOW); + LLFastTimer t(FTM_RENDER_GLOW); LLGLEnable blend(GL_BLEND); LLGLDisable test(GL_ALPHA_TEST); gGL.setSceneBlendType(LLRender::BT_ADD); @@ -98,7 +97,7 @@ void LLDrawPoolSimple::prerender() void LLDrawPoolSimple::beginRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE); + LLFastTimer t(FTM_RENDER_SIMPLE); if (LLPipeline::sUnderWaterRender) { @@ -125,7 +124,7 @@ void LLDrawPoolSimple::beginRenderPass(S32 pass) void LLDrawPoolSimple::endRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE); + LLFastTimer t(FTM_RENDER_SIMPLE); LLRenderPass::endRenderPass(pass); if (mVertexShaderLevel > 0){ @@ -140,7 +139,7 @@ void LLDrawPoolSimple::render(S32 pass) LLGLDisable alpha_test(GL_ALPHA_TEST); { //render simple - LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE); + LLFastTimer t(FTM_RENDER_SIMPLE); gPipeline.enableLightsDynamic(); renderTexture(LLRenderPass::PASS_SIMPLE, getVertexDataMask()); @@ -157,13 +156,13 @@ void LLDrawPoolSimple::render(S32 pass) void LLDrawPoolSimple::beginDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE); + LLFastTimer t(FTM_RENDER_SIMPLE); gDeferredDiffuseProgram.bind(); } void LLDrawPoolSimple::endDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE); + LLFastTimer t(FTM_RENDER_SIMPLE); LLRenderPass::endRenderPass(pass); gDeferredDiffuseProgram.unbind(); @@ -175,7 +174,7 @@ void LLDrawPoolSimple::renderDeferred(S32 pass) LLGLDisable alpha_test(GL_ALPHA_TEST); { //render simple - LLFastTimer t(LLFastTimer::FTM_RENDER_SIMPLE); + LLFastTimer t(FTM_RENDER_SIMPLE); renderTexture(LLRenderPass::PASS_SIMPLE, getVertexDataMask()); } } @@ -195,7 +194,7 @@ void LLDrawPoolGrass::prerender() void LLDrawPoolGrass::beginRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_GRASS); + LLFastTimer t(FTM_RENDER_GRASS); if (LLPipeline::sUnderWaterRender) { @@ -222,7 +221,7 @@ void LLDrawPoolGrass::beginRenderPass(S32 pass) void LLDrawPoolGrass::endRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_GRASS); + LLFastTimer t(FTM_RENDER_GRASS); LLRenderPass::endRenderPass(pass); if (mVertexShaderLevel > 0) @@ -237,7 +236,7 @@ void LLDrawPoolGrass::render(S32 pass) gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); { - LLFastTimer t(LLFastTimer::FTM_RENDER_GRASS); + LLFastTimer t(FTM_RENDER_GRASS); LLGLEnable test(GL_ALPHA_TEST); gGL.setSceneBlendType(LLRender::BT_ALPHA); //render grass @@ -262,7 +261,7 @@ void LLDrawPoolGrass::renderDeferred(S32 pass) gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); { - LLFastTimer t(LLFastTimer::FTM_RENDER_GRASS); + LLFastTimer t(FTM_RENDER_GRASS); gDeferredTreeProgram.bind(); LLGLEnable test(GL_ALPHA_TEST); //render grass @@ -286,7 +285,7 @@ void LLDrawPoolFullbright::prerender() void LLDrawPoolFullbright::beginRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_FULLBRIGHT); + LLFastTimer t(FTM_RENDER_FULLBRIGHT); if (LLPipeline::sUnderWaterRender) { @@ -300,7 +299,7 @@ void LLDrawPoolFullbright::beginRenderPass(S32 pass) void LLDrawPoolFullbright::endRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_FULLBRIGHT); + LLFastTimer t(FTM_RENDER_FULLBRIGHT); LLRenderPass::endRenderPass(pass); if (mVertexShaderLevel > 0) @@ -311,7 +310,7 @@ void LLDrawPoolFullbright::endRenderPass(S32 pass) void LLDrawPoolFullbright::render(S32 pass) { //render fullbright - LLFastTimer t(LLFastTimer::FTM_RENDER_FULLBRIGHT); + LLFastTimer t(FTM_RENDER_FULLBRIGHT); if (mVertexShaderLevel > 0) { fullbright_shader->bind(); diff --git a/indra/newview/lldrawpoolterrain.cpp b/indra/newview/lldrawpoolterrain.cpp index a01c9026c8..345dd6bb84 100644 --- a/indra/newview/lldrawpoolterrain.cpp +++ b/indra/newview/lldrawpoolterrain.cpp @@ -61,6 +61,8 @@ int DebugDetailMap = 0; S32 LLDrawPoolTerrain::sDetailMode = 1; F32 LLDrawPoolTerrain::sDetailScale = DETAIL_SCALE; static LLGLSLShader* sShader = NULL; +static LLFastTimer::DeclareTimer FTM_SHADOW_TERRAIN("Terrain Shadow"); + LLDrawPoolTerrain::LLDrawPoolTerrain(LLViewerTexture *texturep) : LLFacePool(POOL_TERRAIN), @@ -131,7 +133,7 @@ void LLDrawPoolTerrain::prerender() void LLDrawPoolTerrain::beginRenderPass( S32 pass ) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN); + LLFastTimer t(FTM_RENDER_TERRAIN); LLFacePool::beginRenderPass(pass); sShader = LLPipeline::sUnderWaterRender ? @@ -146,7 +148,7 @@ void LLDrawPoolTerrain::beginRenderPass( S32 pass ) void LLDrawPoolTerrain::endRenderPass( S32 pass ) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN); + LLFastTimer t(FTM_RENDER_TERRAIN); LLFacePool::endRenderPass(pass); if (mVertexShaderLevel > 1 && sShader->mShaderLevel > 0) { @@ -162,7 +164,7 @@ S32 LLDrawPoolTerrain::getDetailMode() void LLDrawPoolTerrain::render(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN); + LLFastTimer t(FTM_RENDER_TERRAIN); if (mDrawFace.empty()) { @@ -235,7 +237,7 @@ void LLDrawPoolTerrain::render(S32 pass) void LLDrawPoolTerrain::beginDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN); + LLFastTimer t(FTM_RENDER_TERRAIN); LLFacePool::beginRenderPass(pass); sShader = &gDeferredTerrainProgram; @@ -245,14 +247,14 @@ void LLDrawPoolTerrain::beginDeferredPass(S32 pass) void LLDrawPoolTerrain::endDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN); + LLFastTimer t(FTM_RENDER_TERRAIN); LLFacePool::endRenderPass(pass); sShader->unbind(); } void LLDrawPoolTerrain::renderDeferred(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TERRAIN); + LLFastTimer t(FTM_RENDER_TERRAIN); if (mDrawFace.empty()) { return; @@ -262,7 +264,7 @@ void LLDrawPoolTerrain::renderDeferred(S32 pass) void LLDrawPoolTerrain::beginShadowPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_TERRAIN); + LLFastTimer t(FTM_SHADOW_TERRAIN); LLFacePool::beginRenderPass(pass); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gDeferredShadowProgram.bind(); @@ -270,14 +272,14 @@ void LLDrawPoolTerrain::beginShadowPass(S32 pass) void LLDrawPoolTerrain::endShadowPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_TERRAIN); + LLFastTimer t(FTM_SHADOW_TERRAIN); LLFacePool::endRenderPass(pass); gDeferredShadowProgram.unbind(); } void LLDrawPoolTerrain::renderShadow(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_TERRAIN); + LLFastTimer t(FTM_SHADOW_TERRAIN); if (mDrawFace.empty()) { return; diff --git a/indra/newview/lldrawpooltree.cpp b/indra/newview/lldrawpooltree.cpp index f572e2cb44..5cb1fcb635 100644 --- a/indra/newview/lldrawpooltree.cpp +++ b/indra/newview/lldrawpooltree.cpp @@ -46,6 +46,7 @@ S32 LLDrawPoolTree::sDiffTex = 0; static LLGLSLShader* shader = NULL; +static LLFastTimer::DeclareTimer FTM_SHADOW_TREE("Tree Shadow"); LLDrawPoolTree::LLDrawPoolTree(LLViewerTexture *texturep) : LLFacePool(POOL_TREE), @@ -67,7 +68,7 @@ void LLDrawPoolTree::prerender() void LLDrawPoolTree::beginRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TREES); + LLFastTimer t(FTM_RENDER_TREES); gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); if (LLPipeline::sUnderWaterRender) @@ -91,7 +92,7 @@ void LLDrawPoolTree::beginRenderPass(S32 pass) void LLDrawPoolTree::render(S32 pass) { - LLFastTimer t(LLPipeline::sShadowRender ? LLFastTimer::FTM_SHADOW_TREE : LLFastTimer::FTM_RENDER_TREES); + LLFastTimer t(LLPipeline::sShadowRender ? FTM_SHADOW_TREE : FTM_RENDER_TREES); if (mDrawFace.empty()) { @@ -122,7 +123,7 @@ void LLDrawPoolTree::render(S32 pass) void LLDrawPoolTree::endRenderPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TREES); + LLFastTimer t(FTM_RENDER_TREES); gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); if (gPipeline.canUseWindLightShadersOnObjects()) @@ -136,7 +137,7 @@ void LLDrawPoolTree::endRenderPass(S32 pass) //============================================ void LLDrawPoolTree::beginDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TREES); + LLFastTimer t(FTM_RENDER_TREES); gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); shader = &gDeferredTreeProgram; @@ -150,7 +151,7 @@ void LLDrawPoolTree::renderDeferred(S32 pass) void LLDrawPoolTree::endDeferredPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_RENDER_TREES); + LLFastTimer t(FTM_RENDER_TREES); gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); shader->unbind(); @@ -161,7 +162,7 @@ void LLDrawPoolTree::endDeferredPass(S32 pass) //============================================ void LLDrawPoolTree::beginShadowPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_TREE); + LLFastTimer t(FTM_SHADOW_TREE); gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.5f); gDeferredShadowProgram.bind(); } @@ -173,7 +174,7 @@ void LLDrawPoolTree::renderShadow(S32 pass) void LLDrawPoolTree::endShadowPass(S32 pass) { - LLFastTimer t(LLFastTimer::FTM_SHADOW_TREE); + LLFastTimer t(FTM_SHADOW_TREE); gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); gDeferredShadowProgram.unbind(); } diff --git a/indra/newview/lldrawpoolwater.cpp b/indra/newview/lldrawpoolwater.cpp index 16623ca2b6..fd4dc123d5 100644 --- a/indra/newview/lldrawpoolwater.cpp +++ b/indra/newview/lldrawpoolwater.cpp @@ -138,7 +138,7 @@ void LLDrawPoolWater::endPostDeferredPass(S32 pass) void LLDrawPoolWater::render(S32 pass) { - LLFastTimer ftm(LLFastTimer::FTM_RENDER_WATER); + LLFastTimer ftm(FTM_RENDER_WATER); if (mDrawFace.empty() || LLDrawable::getCurrentFrame() <= 1) { return; diff --git a/indra/newview/lldrawpoolwlsky.cpp b/indra/newview/lldrawpoolwlsky.cpp index 917b6fafbc..c14ca2473b 100644 --- a/indra/newview/lldrawpoolwlsky.cpp +++ b/indra/newview/lldrawpoolwlsky.cpp @@ -42,7 +42,6 @@ #include "llwlparammanager.h" #include "llsky.h" #include "llvowlsky.h" -#include "llagent.h" #include "llviewerregion.h" #include "llface.h" #include "llrender.h" @@ -260,7 +259,7 @@ void LLDrawPoolWLSky::render(S32 pass) { return; } - LLFastTimer ftm(LLFastTimer::FTM_RENDER_WL_SKY); + LLFastTimer ftm(FTM_RENDER_WL_SKY); const F32 camHeightLocal = LLWLParamManager::instance()->getDomeOffset() * LLWLParamManager::instance()->getDomeRadius(); diff --git a/indra/newview/llemote.cpp b/indra/newview/llemote.cpp index bacf3daf5a..c83846215e 100644 --- a/indra/newview/llemote.cpp +++ b/indra/newview/llemote.cpp @@ -39,7 +39,6 @@ #include "llcharacter.h" #include "m3math.h" #include "llvoavatar.h" -#include "llagent.h" //----------------------------------------------------------------------------- // Constants diff --git a/indra/newview/llfasttimerview.cpp b/indra/newview/llfasttimerview.cpp index f383e5301c..b406300a38 100644 --- a/indra/newview/llfasttimerview.cpp +++ b/indra/newview/llfasttimerview.cpp @@ -195,7 +195,8 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask) if(LLFastTimer::sPauseHistory && mBarRect.pointInRect(x, y)) { - mHoverBarIndex = llmin(LLFastTimer::getCurFrameIndex() - 1, MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight())); + mHoverBarIndex = llmin(LLFastTimer::getCurFrameIndex() - 1, + MAX_VISIBLE_HISTORY - ((y - mBarRect.mBottom) * (MAX_VISIBLE_HISTORY + 2) / mBarRect.getHeight())); if (mHoverBarIndex == 0) { return TRUE; @@ -217,9 +218,9 @@ BOOL LLFastTimerView::handleHover(S32 x, S32 y, MASK mask) mHoverID = (*it); mHoverTimer = (*it); mToolTipRect.set(mBarStart[mHoverBarIndex][i], - mBarRect.mBottom + llround(((F32)mHoverBarIndex + 1.f) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f))), + mBarRect.mBottom + llround(((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex + 1)) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f))), mBarEnd[mHoverBarIndex][i], - mBarRect.mBottom + llround((F32)mHoverBarIndex * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f)))); + mBarRect.mBottom + llround((F32)(MAX_VISIBLE_HISTORY - mHoverBarIndex) * ((F32)mBarRect.getHeight() / ((F32)MAX_VISIBLE_HISTORY + 2.f)))); } if ((*it)->getCollapsed()) @@ -248,6 +249,7 @@ BOOL LLFastTimerView::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* stic // tooltips for timer bars if (mHoverTimer) { + localRectToScreen(mToolTipRect, sticky_rect_screen); msg = mHoverTimer->getToolTip(LLFastTimer::NamedTimer::HISTORY_NUM - mScrollIndex - mHoverBarIndex); return TRUE; } @@ -960,9 +962,14 @@ void LLFastTimerView::draw() LLView::draw(); } -F64 LLFastTimerView::getTime(LLFastTimer::NamedTimer::FrameState& id) +F64 LLFastTimerView::getTime(const std::string& name) { - return (F64)id.mTimer->getCountAverage() / (F64)LLFastTimer::countsPerSecond(); + const LLFastTimer::NamedTimer* timerp = LLFastTimer::getTimerByName(name); + if (timerp) + { + return (F64)timerp->getCountAverage() / (F64)LLFastTimer::countsPerSecond(); + } + return 0.0; } //static diff --git a/indra/newview/llfasttimerview.h b/indra/newview/llfasttimerview.h index 78ca7b50d6..f301888984 100644 --- a/indra/newview/llfasttimerview.h +++ b/indra/newview/llfasttimerview.h @@ -62,7 +62,7 @@ public: virtual void draw(); LLFastTimer::NamedTimer* getLegendID(S32 y); - F64 getTime(LLFastTimer::NamedTimer::FrameState& id); + F64 getTime(const std::string& name); private: typedef std::vector<std::vector<S32> > bar_positions_t; diff --git a/indra/newview/llfavoritesbar.cpp b/indra/newview/llfavoritesbar.cpp index 9cb3ea127f..70ee5d395e 100644 --- a/indra/newview/llfavoritesbar.cpp +++ b/indra/newview/llfavoritesbar.cpp @@ -36,13 +36,20 @@ #include "llbutton.h" #include "llfloaterreg.h" +#include "llfocusmgr.h" #include "llinventory.h" +#include "lllandmarkactions.h" +#include "lltrans.h" #include "lluictrlfactory.h" #include "llmenugl.h" #include "llagent.h" +#include "llclipboard.h" +#include "llinventoryclipboard.h" #include "llinventorybridge.h" #include "llinventorymodel.h" +#include "llfloaterworldmap.h" +#include "lllandmarkactions.h" #include "llsidetray.h" #include "lltoggleablemenu.h" #include "llviewerinventory.h" @@ -51,6 +58,71 @@ static LLDefaultChildRegistry::Register<LLFavoritesBarCtrl> r("favorites_bar"); +const S32 DROP_DOWN_MENU_WIDTH = 250; + +/** + * This class is needed to override LLButton default handleToolTip function and + * show SLURL as button tooltip. + * *NOTE: dzaporozhan: This is a workaround. We could set tooltips for buttons + * in createButtons function but landmark data is not available when Favorites Bar is + * created. Thats why we are requesting landmark data after + */ +class LLFavoriteLandmarkButton : public LLButton +{ +public: + + /** + * Requests landmark data from server and shows landmark SLURL as tooltip. + */ + BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect) + { + if(LLUI::sShowXUINames) + { + return LLButton::handleToolTip(x, y, msg, sticky_rect); + } + + if(!mLoaded) + { + LLVector3d g_pos; + if(LLLandmarkActions::getLandmarkGlobalPos(mLandmarkID, g_pos)) + { + LLLandmarkActions::getSLURLfromPosGlobal(g_pos, + boost::bind(&LLFavoriteLandmarkButton::landmarkNameCallback, this, _1), false); + } + } + + msg = mSLURL; + return TRUE; + } + + void landmarkNameCallback(const std::string& name) + { + mSLURL = name; + mLoaded = true; + } + + void setLandmarkID(const LLUUID& id){ mLandmarkID = id; } + +protected: + + LLFavoriteLandmarkButton(const LLButton::Params& p) + : LLButton(p) + , mLandmarkID(LLUUID::null) + , mSLURL("(Loading...)") + , mLoaded(false) + { + static std::string loading_tooltip = LLTrans::getString("favorite_landmark_loading_tooltip"); + mSLURL = loading_tooltip; + } + + friend class LLUICtrlFactory; + +private: + LLUUID mLandmarkID; + std::string mSLURL; + bool mLoaded; +}; + // updateButtons's helper struct LLFavoritesSort { @@ -71,11 +143,17 @@ struct LLFavoritesSort } }; +LLFavoritesBarCtrl::Params::Params() +: chevron_button_tool_tip("chevron_button_tool_tip") +{ +} + LLFavoritesBarCtrl::LLFavoritesBarCtrl(const LLFavoritesBarCtrl::Params& p) : LLUICtrl(p), mFont(p.font.isProvided() ? p.font() : LLFontGL::getFontSansSerifSmall()), mPopupMenuHandle(), - mInventoryItemsPopupMenuHandle() + mInventoryItemsPopupMenuHandle(), + mChevronButtonToolTip(p.chevron_button_tool_tip) { // Register callback for menus with current registrar (will be parent panel's registrar) LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Favorites.DoToSelected", @@ -282,7 +360,7 @@ void LLFavoritesBarCtrl::updateButtons(U32 bar_width) if (mFirstDropDownItem != count) { // Chevron button should stay right aligned - LLView *chevron_button = getChildView(std::string(">>"), FALSE, FALSE); + LLView *chevron_button = findChildView(std::string(">>"), FALSE); if (chevron_button) { LLRect rect; @@ -314,6 +392,7 @@ void LLFavoritesBarCtrl::updateButtons(U32 bar_width) bparams.tab_stop(false); bparams.font(mFont); bparams.name(">>"); + bparams.tool_tip(mChevronButtonToolTip); bparams.click_callback.function(boost::bind(&LLFavoritesBarCtrl::showDropDownMenu, this)); addChildInBack(LLUICtrlFactory::create<LLButton> (bparams)); @@ -324,7 +403,7 @@ void LLFavoritesBarCtrl::updateButtons(U32 bar_width) else { // Hide chevron button if all items are visible on bar - LLView *chevron_button = getChildView(std::string(">>"), FALSE, FALSE); + LLView *chevron_button = findChildView(std::string(">>"), FALSE); if (chevron_button) { chevron_button->setVisible(FALSE); @@ -341,13 +420,15 @@ void LLFavoritesBarCtrl::createButtons(const LLInventoryModel::item_array_t &ite { LLInventoryItem* item = items.get(i); - LLButton* fav_btn = LLUICtrlFactory::defaultBuilder<LLButton>(buttonXMLNode, this, NULL); + LLFavoriteLandmarkButton* fav_btn = LLUICtrlFactory::defaultBuilder<LLFavoriteLandmarkButton>(buttonXMLNode, this, NULL); if (NULL == fav_btn) { llwarns << "Unable to create button for landmark: " << item->getName() << llendl; continue; } + fav_btn->setLandmarkID(item->getUUID()); + // change only left and save bottom fav_btn->setOrigin(curr_x, fav_btn->getRect().mBottom); fav_btn->setFont(mFont); @@ -355,7 +436,7 @@ void LLFavoritesBarCtrl::createButtons(const LLInventoryModel::item_array_t &ite fav_btn->setLabel(item->getName()); fav_btn->setToolTip(item->getName()); fav_btn->setCommitCallback(boost::bind(&LLFavoritesBarCtrl::onButtonClick, this, item->getUUID())); - fav_btn->setRightClickedCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this, item->getUUID(), _1, _2, _3,_4 )); + fav_btn->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this, item->getUUID(), _1, _2, _3,_4 )); sendChildToBack(fav_btn); curr_x += buttonWidth + buttonHGap; @@ -401,6 +482,7 @@ void LLFavoritesBarCtrl::showDropDownMenu() menu_p.can_tear_off(false); menu_p.visible(false); menu_p.scrollable(true); + menu_p.max_scrollable_items = 10; LLToggleableMenu* menu = LLUICtrlFactory::create<LLToggleableMenu>(menu_p); @@ -411,18 +493,8 @@ void LLFavoritesBarCtrl::showDropDownMenu() if(menu) { - if (menu->getClosedByButtonClick()) - { - menu->resetClosedByButtonClick(); - return; - } - - if (menu->getVisible()) - { - menu->setVisible(FALSE); - menu->resetClosedByButtonClick(); + if (!menu->toggleVisibility()) return; - } LLInventoryModel::item_array_t items; @@ -469,10 +541,8 @@ void LLFavoritesBarCtrl::showDropDownMenu() menu->empty(); - U32 max_width = 0; - - // Menu will not be wider, than bar - S32 bar_width = getRect().getWidth(); + U32 max_width = llmin(DROP_DOWN_MENU_WIDTH, getRect().getWidth()); + U32 widest_item = 0; for(S32 i = mFirstDropDownItem; i < count; i++) { @@ -485,14 +555,14 @@ void LLFavoritesBarCtrl::showDropDownMenu() item_params.on_click.function(boost::bind(&LLFavoritesBarCtrl::onButtonClick, this, item->getUUID())); LLMenuItemCallGL *menu_item = LLUICtrlFactory::create<LLMenuItemCallGL>(item_params); - menu_item->setRightClickedCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this,item->getUUID(),_1,_2,_3,_4)); + menu_item->setRightMouseDownCallback(boost::bind(&LLFavoritesBarCtrl::onButtonRightClick, this,item->getUUID(),_1,_2,_3,_4)); // Check whether item name wider than menu - if ((S32) menu_item->getNominalWidth() > bar_width) + if (menu_item->getNominalWidth() > max_width) { S32 chars_total = item_name.length(); S32 chars_fitted = 1; menu_item->setLabel(LLStringExplicit("")); - S32 label_space = bar_width - menu_item->getFont()->getWidth("...") - + S32 label_space = max_width - menu_item->getFont()->getWidth("...") - menu_item->getNominalWidth(); // This returns width of menu item with empty label (pad pixels) while (chars_fitted < chars_total && menu_item->getFont()->getWidth(item_name, 0, chars_fitted) < label_space) @@ -503,21 +573,17 @@ void LLFavoritesBarCtrl::showDropDownMenu() menu_item->setLabel(item_name.substr(0, chars_fitted) + "..."); } - - max_width = llmax(max_width, menu_item->getNominalWidth()); + widest_item = llmax(widest_item, menu_item->getNominalWidth()); menu->addChild(menu_item); } - // Menu will not be wider, than bar - max_width = llmin((S32)max_width, bar_width); - menu->buildDrawLabels(); menu->updateParent(LLMenuGL::sMenuContainer); menu->setButtonRect(mChevronRect, this); - LLMenuGL::showPopup(this, menu, getRect().getWidth() - max_width, 0); + LLMenuGL::showPopup(this, menu, getRect().getWidth() - widest_item, 0); } } @@ -544,10 +610,20 @@ void LLFavoritesBarCtrl::onButtonRightClick( LLUUID item_id,LLView* fav_button,S return; } + // Release mouse capture so hover events go to the popup menu + // because this is happening during a mouse down. + gFocusMgr.setMouseCapture(NULL); + menu->updateParent(LLMenuGL::sMenuContainer); LLMenuGL::showPopup(fav_button, menu, x, y); } +void copy_slurl_to_clipboard_cb(std::string& slurl) +{ + gClipboard.copyFromString(utf8str_to_wstring(slurl)); +} + + void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) { std::string action = userdata.asString(); @@ -569,13 +645,102 @@ void LLFavoritesBarCtrl::doToSelected(const LLSD& userdata) LLSideTray::getInstance()->showPanel("panel_places", key); } - else if (action == "rename") + else if (action == "copy_slurl") + { + LLVector3d posGlobal; + LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + + if (!posGlobal.isExactlyZero()) + { + LLLandmarkActions::getSLURLfromPosGlobal(posGlobal, copy_slurl_to_clipboard_cb); + } + } + else if (action == "show_on_map") + { + LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); + + LLVector3d posGlobal; + LLLandmarkActions::getLandmarkGlobalPos(mSelectedItemID, posGlobal); + + if (!posGlobal.isExactlyZero() && worldmap_instance) + { + worldmap_instance->trackLocation(posGlobal); + LLFloaterReg::showInstance("world_map", "center"); + } + } + else if (action == "cut") { - // Would need to re-implement this: - // folder->startRenamingSelectedItem(); + } + else if (action == "copy") + { + LLInventoryClipboard::instance().store(mSelectedItemID); + } + else if (action == "paste") + { + pastFromClipboard(); } else if (action == "delete") { gInventory.removeItem(mSelectedItemID); } } + +BOOL LLFavoritesBarCtrl::isClipboardPasteable() const +{ + if (!LLInventoryClipboard::instance().hasContents()) + { + return FALSE; + } + + LLDynamicArray<LLUUID> objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + for(S32 i = 0; i < count; i++) + { + const LLUUID &item_id = objects.get(i); + + // Can't paste folders + const LLInventoryCategory *cat = gInventory.getCategory(item_id); + if (cat) + { + return FALSE; + } + + const LLInventoryItem *item = gInventory.getItem(item_id); + if (item && LLAssetType::AT_LANDMARK != item->getType()) + { + return FALSE; + } + } + return TRUE; +} + +void LLFavoritesBarCtrl::pastFromClipboard() const +{ + LLInventoryModel* model = &gInventory; + if(model && isClipboardPasteable()) + { + LLInventoryItem* item = NULL; + LLDynamicArray<LLUUID> objects; + LLInventoryClipboard::instance().retrieve(objects); + S32 count = objects.count(); + LLUUID parent_id(mFavoriteFolderId); + for(S32 i = 0; i < count; i++) + { + item = model->getItem(objects.get(i)); + if (item) + { + copy_inventory_item( + gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + parent_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } + } + } +} + + +// EOF diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index 7da33e2f6e..824b396add 100644 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -40,7 +40,12 @@ class LLFavoritesBarCtrl : public LLUICtrl, public LLInventoryObserver { public: - struct Params : public LLUICtrl::Params{}; + struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> + { + Optional<std::string> chevron_button_tool_tip; + Params(); + }; + protected: LLFavoritesBarCtrl(const Params&); friend class LLUICtrlFactory; @@ -69,6 +74,9 @@ protected: void onButtonRightClick(LLUUID id,LLView* button,S32 x,S32 y,MASK mask); void doToSelected(const LLSD& userdata); + BOOL isClipboardPasteable() const; + void pastFromClipboard() const; + void showDropDownMenu(); @@ -84,6 +92,8 @@ protected: LLUUID mSelectedItemID; LLRect mChevronRect; + + std::string mChevronButtonToolTip; }; diff --git a/indra/newview/llflexibleobject.cpp b/indra/newview/llflexibleobject.cpp index bafb69a835..216bca8262 100644 --- a/indra/newview/llflexibleobject.cpp +++ b/indra/newview/llflexibleobject.cpp @@ -261,6 +261,7 @@ void LLVolumeImplFlexible::onSetVolume(const LLVolumeParams &volume_params, cons // updated every time step. In the future, perhaps there could be an // optimization similar to what Havok does for objects that are stationary. //--------------------------------------------------------------------------------- +static LLFastTimer::DeclareTimer FTM_FLEXIBLE_UPDATE("Update Flexies"); BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { if (mVO->mDrawable.isNull()) @@ -279,7 +280,7 @@ BOOL LLVolumeImplFlexible::doIdleUpdate(LLAgent &agent, LLWorld &world, const F6 parent->mDrawable->mQuietCount = 0; } - LLFastTimer ftm(LLFastTimer::FTM_FLEXIBLE_UPDATE); + LLFastTimer ftm(FTM_FLEXIBLE_UPDATE); S32 new_res = mAttributes->getSimulateLOD(); diff --git a/indra/newview/llfloaterabout.cpp b/indra/newview/llfloaterabout.cpp index 2cdb149f9a..30a5f9f36a 100644 --- a/indra/newview/llfloaterabout.cpp +++ b/indra/newview/llfloaterabout.cpp @@ -42,7 +42,7 @@ #include "llcurl.h"
#include "llimagej2c.h"
-#include "audioengine.h"
+#include "llaudioengine.h"
#include "llviewertexteditor.h"
#include "llviewercontrol.h"
@@ -58,9 +58,10 @@ #include "lltrans.h"
#include "llappviewer.h"
#include "llglheaders.h"
-#include "llmediamanager.h"
#include "llwindow.h"
+#include "llbutton.h"
+
#if LL_WINDOWS
#include "lldxhardware.h"
#endif
@@ -101,20 +102,18 @@ BOOL LLFloaterAbout::postBuild() support_widget->setParseHTML(TRUE);
// Text styles for release notes hyperlinks
- LLStyleSP viewer_link_style(new LLStyle);
- viewer_link_style->setVisible(true);
- viewer_link_style->setFontName(LLStringUtil::null);
- viewer_link_style->setLinkHREF(get_viewer_release_notes_url());
- viewer_link_style->setColor(LLUIColorTable::instance().getColor("HTMLLinkColor"));
+ LLStyle::Params link_style_params;
+ link_style_params.color.control = "HTMLLinkColor";
+ link_style_params.link_href = get_viewer_release_notes_url();
// Version string
- std::string version = LLTrans::getString("SECOND_LIFE_VIEWER")
+ std::string version = LLTrans::getString("APP_NAME")
+ llformat(" %d.%d.%d (%d) %s %s (%s)\n",
LL_VERSION_MAJOR, LL_VERSION_MINOR, LL_VERSION_PATCH, LL_VIEWER_BUILD,
__DATE__, __TIME__,
gSavedSettings.getString("VersionChannelName").c_str());
support_widget->appendColoredText(version, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor"));
- support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, viewer_link_style);
+ support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, link_style_params);
std::string support;
support.append("\n\n");
@@ -131,11 +130,9 @@ BOOL LLFloaterAbout::postBuild() LLViewerRegion* region = gAgent.getRegion();
if (region)
{
- LLStyleSP server_link_style(new LLStyle);
- server_link_style->setVisible(true);
- server_link_style->setFontName(LLStringUtil::null);
- server_link_style->setLinkHREF(region->getCapability("ServerReleaseNotes"));
- server_link_style->setColor(LLUIColorTable::instance().getColor("HTMLLinkColor"));
+ LLStyle::Params server_link_style_params;
+ server_link_style_params.color.control = "HTMLLinkColor";
+ server_link_style_params.link_href = region->getCapability("ServerReleaseNotes");
const LLVector3d &pos = gAgent.getPositionGlobal();
LLUIString pos_text = getString("you_are_at");
@@ -158,7 +155,7 @@ BOOL LLFloaterAbout::postBuild() support.append("\n");
support_widget->appendColoredText(support, FALSE, FALSE, LLUIColorTable::instance().getColor("TextFgReadOnlyColor"));
- support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style);
+ support_widget->appendStyledText(LLTrans::getString("ReleaseNotes"), false, false, server_link_style_params);
support = "\n\n";
}
@@ -224,18 +221,10 @@ BOOL LLFloaterAbout::postBuild() support.append( gAudiop ? gAudiop->getDriverName(want_fullname) : getString("none") );
support.append("\n");
- LLMediaManager *mgr = LLMediaManager::getInstance();
- if (mgr)
- {
- LLMediaBase *media_source = mgr->createSourceFromMimeType("http", "text/html");
- if (media_source)
- {
- support.append(getString("LLMozLibVersion") + " ");
- support.append(media_source->getVersion());
- support.append("\n");
- mgr->destroySource(media_source);
- }
- }
+ // TODO: Implement media plugin version query
+
+ support.append(getString("LLQtWebkitVersion") + " ");
+ support.append("\n");
if (gPacketsIn > 0)
{
@@ -250,13 +239,9 @@ BOOL LLFloaterAbout::postBuild() // Fix views
support_widget->setCursorPos(0);
support_widget->setEnabled(FALSE);
- support_widget->setTakesFocus(TRUE);
- support_widget->setHandleEditKeysDirectly(TRUE);
credits_widget->setCursorPos(0);
credits_widget->setEnabled(FALSE);
- credits_widget->setTakesFocus(TRUE);
- credits_widget->setHandleEditKeysDirectly(TRUE);
return TRUE;
}
diff --git a/indra/newview/llfloaterbuyland.cpp b/indra/newview/llfloaterbuyland.cpp index 3d1b7965a1..7075719299 100644 --- a/indra/newview/llfloaterbuyland.cpp +++ b/indra/newview/llfloaterbuyland.cpp @@ -566,32 +566,25 @@ void LLFloaterBuyLandUI::onChangeAgreeCovenant(LLUICtrl* ctrl, void* user_data) void LLFloaterBuyLandUI::updateFloaterCovenantText(const std::string &string, const LLUUID& asset_id) { LLViewerTextEditor* editor = getChild<LLViewerTextEditor>("covenant_editor"); - if (editor) - { - editor->setHandleEditKeysDirectly(FALSE); - editor->setText(string); + editor->setText(string); - LLCheckBoxCtrl* check = getChild<LLCheckBoxCtrl>("agree_covenant"); - LLTextBox* box = getChild<LLTextBox>("covenant_text"); - if(check && box) - { - if (asset_id.isNull()) - { - check->set(true); - check->setEnabled(false); - refreshUI(); + LLCheckBoxCtrl* check = getChild<LLCheckBoxCtrl>("agree_covenant"); + LLTextBox* box = getChild<LLTextBox>("covenant_text"); + if (asset_id.isNull()) + { + check->set(true); + check->setEnabled(false); + refreshUI(); - // remove the line stating that you must agree - box->setVisible(FALSE); - } - else - { - check->setEnabled(true); + // remove the line stating that you must agree + box->setVisible(FALSE); + } + else + { + check->setEnabled(true); - // remove the line stating that you must agree - box->setVisible(TRUE); - } - } + // remove the line stating that you must agree + box->setVisible(TRUE); } } diff --git a/indra/newview/llfloaterbuyland.h b/indra/newview/llfloaterbuyland.h index 7df07f752d..00d44035ae 100644 --- a/indra/newview/llfloaterbuyland.h +++ b/indra/newview/llfloaterbuyland.h @@ -35,7 +35,6 @@ class LLFloater; class LLViewerRegion; -class LLViewerTextEditor; class LLParcelSelection; class LLFloaterBuyLand diff --git a/indra/newview/llfloaterchat.cpp b/indra/newview/llfloaterchat.cpp index 742cc5c5de..0dee3a1e83 100644 --- a/indra/newview/llfloaterchat.cpp +++ b/indra/newview/llfloaterchat.cpp @@ -71,7 +71,7 @@ #include "llstylemap.h" // linden library includes -#include "audioengine.h" +#include "llaudioengine.h" #include "llchat.h" #include "llfontgl.h" #include "llrect.h" @@ -172,8 +172,7 @@ void add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, const LLColor4& { std::string start_line = line.substr(0, chat.mFromName.length() + 1); line = line.substr(chat.mFromName.length() + 1); - const LLStyleSP &sourceStyle = LLStyleMap::instance().lookup(chat.mFromID,chat.mURL); - edit->appendStyledText(start_line, false, prepend_newline, sourceStyle); + edit->appendStyledText(start_line, false, prepend_newline, LLStyleMap::instance().lookup(chat.mFromID,chat.mURL)); prepend_newline = false; } edit->appendColoredText(line, false, prepend_newline, color); diff --git a/indra/newview/llfloaterchat.h b/indra/newview/llfloaterchat.h index e8af48d095..6ba3165d6a 100644 --- a/indra/newview/llfloaterchat.h +++ b/indra/newview/llfloaterchat.h @@ -42,14 +42,8 @@ #include "lllogchat.h" class LLChat; -class LLViewerTextEditor; -class LLMessageSystem; -class LLUUID; -class LLCheckBoxCtrl; class LLPanelActiveSpeakers; class LLLogChat; -class LLVector3d; -class LLWindow; enum ELogOptions { diff --git a/indra/newview/llfloaterfriends.cpp b/indra/newview/llfloaterfriends.cpp index 1e8e7bad74..eb73bd6d8f 100644 --- a/indra/newview/llfloaterfriends.cpp +++ b/indra/newview/llfloaterfriends.cpp @@ -404,7 +404,7 @@ void LLPanelFriends::refreshNames(U32 changed_mask) // Changed item in place, need to request sort and update columns // because we might have changed data in a column on which the user // has already sorted. JC - mFriendsList->sortItems(); + mFriendsList->updateSort(); // re-select items mFriendsList->selectMultiple(selected_ids); diff --git a/indra/newview/llfloatergroups.cpp b/indra/newview/llfloatergroups.cpp index f49f854620..7a88612f1a 100644 --- a/indra/newview/llfloatergroups.cpp +++ b/indra/newview/llfloatergroups.cpp @@ -81,7 +81,18 @@ void LLFloaterGroupPicker::setPowersMask(U64 powers_mask) BOOL LLFloaterGroupPicker::postBuild() { - init_group_list(getChild<LLScrollListCtrl>("group list"), gAgent.getGroupID(), mPowersMask); + LLScrollListCtrl* list_ctrl = getChild<LLScrollListCtrl>("group list"); + init_group_list(list_ctrl, gAgent.getGroupID(), mPowersMask); + + // Remove group "none" from list. Group "none" is added in init_group_list(). + // Some UI elements use group "none", we need to manually delete it here. + // Group "none" ID is LLUUID:null. + LLCtrlListInterface* group_list = list_ctrl->getListInterface(); + if(group_list) + { + group_list->selectByValue(LLUUID::null); + group_list->operateOnSelection(LLCtrlListInterface::OP_DELETE); + } childSetAction("OK", onBtnOK, this); diff --git a/indra/newview/llfloaterhandler.cpp b/indra/newview/llfloaterhandler.cpp index f229d30488..e50a09ed86 100644 --- a/indra/newview/llfloaterhandler.cpp +++ b/indra/newview/llfloaterhandler.cpp @@ -31,7 +31,7 @@ #include "llfloaterhandler.h" #include "llfloater.h" -#include "llwebbrowserctrl.h" +#include "llmediactrl.h" // register with dispatch via global object LLFloaterHandler gFloaterHandler; @@ -54,7 +54,7 @@ LLFloater* get_parent_floater(LLView* view) } -bool LLFloaterHandler::handle(const LLSD ¶ms, const LLSD &query_map, LLWebBrowserCtrl *web) +bool LLFloaterHandler::handle(const LLSD ¶ms, const LLSD &query_map, LLMediaCtrl *web) { if (params.size() < 2) return false; LLFloater* floater = NULL; diff --git a/indra/newview/llfloaterhandler.h b/indra/newview/llfloaterhandler.h index b08f1f35b4..31ea80c12c 100644 --- a/indra/newview/llfloaterhandler.h +++ b/indra/newview/llfloaterhandler.h @@ -39,7 +39,7 @@ class LLFloaterHandler { public: LLFloaterHandler() : LLCommandHandler("floater", true) { } - bool handle(const LLSD& params, const LLSD& query_map, LLWebBrowserCtrl* web); + bool handle(const LLSD& params, const LLSD& query_map, LLMediaCtrl* web); }; #endif diff --git a/indra/newview/llfloaterhud.cpp b/indra/newview/llfloaterhud.cpp index 4dcf726c9a..047dc2fa92 100644 --- a/indra/newview/llfloaterhud.cpp +++ b/indra/newview/llfloaterhud.cpp @@ -36,7 +36,7 @@ // Viewer libs #include "llviewercontrol.h" -#include "llwebbrowserctrl.h" +#include "llmediactrl.h" #include "llalertdialog.h" // Linden libs @@ -76,7 +76,7 @@ LLFloaterHUD::LLFloaterHUD(const LLSD& key) BOOL LLFloaterHUD::postBuild() { - mWebBrowser = getChild<LLWebBrowserCtrl>("floater_hud_browser" ); + mWebBrowser = getChild<LLMediaCtrl>("floater_hud_browser" ); if (mWebBrowser) { // Open links in internal browser diff --git a/indra/newview/llfloaterhud.h b/indra/newview/llfloaterhud.h index 4772735afc..23ff82362a 100644 --- a/indra/newview/llfloaterhud.h +++ b/indra/newview/llfloaterhud.h @@ -35,7 +35,7 @@ #include "llfloater.h" -class LLWebBrowserCtrl; +class LLMediaCtrl; class LLFloaterHUD : public LLFloater { @@ -50,7 +50,7 @@ private: /*virtual*/ ~LLFloaterHUD(); private: - LLWebBrowserCtrl* mWebBrowser; ///< the actual web browser control + LLMediaCtrl* mWebBrowser; ///< the actual web browser control }; #endif // LL_LLFLOATERHUD_H diff --git a/indra/newview/llfloaterinventory.cpp b/indra/newview/llfloaterinventory.cpp index 718719fe57..45a42f994d 100644 --- a/indra/newview/llfloaterinventory.cpp +++ b/indra/newview/llfloaterinventory.cpp @@ -1000,7 +1000,7 @@ BOOL LLFloaterInventory::handleDragAndDrop(S32 x, S32 y, MASK mask, BOOL drop, { // Check to see if we are auto scrolling from the last frame LLInventoryPanel* panel = (LLInventoryPanel*)this->getActivePanel(); - BOOL needsToScroll = panel->getScrollableContainer()->needsToScroll(x, y, LLScrollContainer::VERTICAL); + BOOL needsToScroll = panel->getScrollableContainer()->autoScroll(x, y); if(mFilterTabs) { if(needsToScroll) @@ -1138,10 +1138,10 @@ const std::string& get_item_icon_name(LLAssetType::EType asset_type, idx = LANDMARK_ICON_NAME; break; case LLAssetType::AT_LINK: - idx = BODYPART_ICON_NAME; // Seraph replace this with broken item link icon + idx = LINKITEM_ICON_NAME; break; case LLAssetType::AT_LINK_FOLDER: - idx = BODYPART_ICON_NAME; // Seraph replace this with broken folder link icon + idx = LINKFOLDER_ICON_NAME; break; default: break; @@ -1320,9 +1320,11 @@ LLInventoryFilter::EFolderShow LLInventoryPanel::getShowFolderState() return mFolders->getFilter()->getShowFolderState(); } +static LLFastTimer::DeclareTimer FTM_REFRESH("Inventory Refresh"); + void LLInventoryPanel::modelChanged(U32 mask) { - LLFastTimer t2(LLFastTimer::FTM_REFRESH); + LLFastTimer t2(FTM_REFRESH); bool handled = false; if(mask & LLInventoryObserver::LABEL) @@ -1684,6 +1686,7 @@ void LLInventoryPanel::onSelectionChange(const std::deque<LLFolderViewItem*>& it fv->startRenamingSelectedItem(); } } + // Seraph - Put determineFolderType in here for ensemble typing? } //---------------------------------------------------------------------------- diff --git a/indra/newview/llfloaterland.cpp b/indra/newview/llfloaterland.cpp index c5e07c6596..4cd09faaaf 100644 --- a/indra/newview/llfloaterland.cpp +++ b/indra/newview/llfloaterland.cpp @@ -55,6 +55,7 @@ #include "lllineeditor.h" #include "llnamelistctrl.h" #include "llnotify.h" +#include "llpanellandaudio.h" #include "llpanellandmedia.h" #include "llradiogroup.h" #include "llscrolllistctrl.h" @@ -225,6 +226,7 @@ LLFloaterLand::LLFloaterLand(const LLSD& seed) mFactoryMap["land_covenant_panel"] = LLCallbackMap(createPanelLandCovenant, this); mFactoryMap["land_objects_panel"] = LLCallbackMap(createPanelLandObjects, this); mFactoryMap["land_options_panel"] = LLCallbackMap(createPanelLandOptions, this); + mFactoryMap["land_audio_panel"] = LLCallbackMap(createPanelLandAudio, this); mFactoryMap["land_media_panel"] = LLCallbackMap(createPanelLandMedia, this); mFactoryMap["land_access_panel"] = LLCallbackMap(createPanelLandAccess, this); @@ -265,6 +267,7 @@ void LLFloaterLand::refresh() mPanelGeneral->refresh(); mPanelObjects->refresh(); mPanelOptions->refresh(); + mPanelAudio->refresh(); mPanelMedia->refresh(); mPanelAccess->refresh(); } @@ -304,6 +307,14 @@ void* LLFloaterLand::createPanelLandOptions(void* data) } // static +void* LLFloaterLand::createPanelLandAudio(void* data) +{ + LLFloaterLand* self = (LLFloaterLand*)data; + self->mPanelAudio = new LLPanelLandAudio(self->mParcel); + return self->mPanelAudio; +} + +// static void* LLFloaterLand::createPanelLandMedia(void* data) { LLFloaterLand* self = (LLFloaterLand*)data; @@ -2847,11 +2858,7 @@ void LLPanelLandCovenant::updateCovenantText(const std::string &string) if (self) { LLViewerTextEditor* editor = self->getChild<LLViewerTextEditor>("covenant_editor"); - if (editor) - { - editor->setHandleEditKeysDirectly(TRUE); - editor->setText(string); - } + editor->setText(string); } } diff --git a/indra/newview/llfloaterland.h b/indra/newview/llfloaterland.h index 7fc5ed0c9e..749c395147 100644 --- a/indra/newview/llfloaterland.h +++ b/indra/newview/llfloaterland.h @@ -59,12 +59,12 @@ class LLTextBox; class LLTextEditor; class LLTextureCtrl; class LLUIImage; -class LLViewerTextEditor; class LLParcelSelection; class LLPanelLandGeneral; class LLPanelLandObjects; class LLPanelLandOptions; +class LLPanelLandAudio; class LLPanelLandMedia; class LLPanelLandAccess; class LLPanelLandBan; @@ -100,6 +100,7 @@ protected: static void* createPanelLandCovenant(void* data); static void* createPanelLandObjects(void* data); static void* createPanelLandOptions(void* data); + static void* createPanelLandAudio(void* data); static void* createPanelLandMedia(void* data); static void* createPanelLandAccess(void* data); static void* createPanelLandBan(void* data); @@ -113,6 +114,7 @@ protected: LLPanelLandGeneral* mPanelGeneral; LLPanelLandObjects* mPanelObjects; LLPanelLandOptions* mPanelOptions; + LLPanelLandAudio* mPanelAudio; LLPanelLandMedia* mPanelMedia; LLPanelLandAccess* mPanelAccess; LLPanelLandCovenant* mPanelCovenant; diff --git a/indra/newview/llfloatermediabrowser.cpp b/indra/newview/llfloatermediabrowser.cpp new file mode 100644 index 0000000000..c580cdef8a --- /dev/null +++ b/indra/newview/llfloatermediabrowser.cpp @@ -0,0 +1,398 @@ +/** + * @file llfloaterhtmlhelp.cpp + * @brief HTML Help floater - uses embedded web browser control + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-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 "llviewerprecompiledheaders.h" + +#include "llfloatermediabrowser.h" +#include "llfloaterhtml.h" + +#include "llfloaterreg.h" +#include "llparcel.h" +#include "llpluginclassmedia.h" +#include "lluictrlfactory.h" +#include "llmediactrl.h" +#include "llviewerwindow.h" +#include "llviewercontrol.h" +#include "llviewerparcelmgr.h" +#include "llweb.h" +#include "llui.h" +#include "roles_constants.h" + +#include "llurlhistory.h" +#include "llmediactrl.h" +#include "llviewermedia.h" +#include "llviewerparcelmedia.h" +#include "llcombobox.h" + + +// TEMP +#include "llsdutil.h" + +LLFloaterMediaBrowser::LLFloaterMediaBrowser(const LLSD& key) + : LLFloater(key) +{ +// LLUICtrlFactory::getInstance()->buildFloater(this, "floater_media_browser.xml"); + +} + +void LLFloaterMediaBrowser::draw() +{ + childSetEnabled("go", !mAddressCombo->getValue().asString().empty()); + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if(parcel) + { + childSetVisible("parcel_owner_controls", LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_MEDIA)); + childSetEnabled("assign", !mAddressCombo->getValue().asString().empty()); + } + bool show_time_controls = false; + bool media_playing = false; + if(mBrowser) + { + LLPluginClassMedia* media_plugin = mBrowser->getMediaPlugin(); + if(media_plugin) + { + show_time_controls = media_plugin->pluginSupportsMediaTime(); + media_playing = media_plugin->getStatus() == LLPluginClassMediaOwner::MEDIA_PLAYING; + } + } + childSetVisible("rewind", show_time_controls); + childSetVisible("play", show_time_controls && ! media_playing); + childSetVisible("pause", show_time_controls && media_playing); + childSetVisible("stop", show_time_controls); + childSetVisible("seek", show_time_controls); + + childSetEnabled("play", ! media_playing); + childSetEnabled("stop", media_playing); + + childSetEnabled("back", mBrowser->canNavigateBack()); + childSetEnabled("forward", mBrowser->canNavigateForward()); + + LLFloater::draw(); +} + +BOOL LLFloaterMediaBrowser::postBuild() +{ + mBrowser = getChild<LLMediaCtrl>("browser"); + mBrowser->addObserver(this); + + mAddressCombo = getChild<LLComboBox>("address"); + mAddressCombo->setCommitCallback(onEnterAddress, this); + + childSetAction("back", onClickBack, this); + childSetAction("forward", onClickForward, this); + childSetAction("reload", onClickRefresh, this); + childSetAction("rewind", onClickRewind, this); + childSetAction("play", onClickPlay, this); + childSetAction("stop", onClickStop, this); + childSetAction("pause", onClickPlay, this); + childSetAction("seek", onClickSeek, this); + childSetAction("go", onClickGo, this); + childSetAction("close", onClickClose, this); + childSetAction("open_browser", onClickOpenWebBrowser, this); + childSetAction("assign", onClickAssign, this); + + buildURLHistory(); + return TRUE; +} + +void LLFloaterMediaBrowser::buildURLHistory() +{ + LLCtrlListInterface* url_list = childGetListInterface("address"); + if (url_list) + { + url_list->operateOnAll(LLCtrlListInterface::OP_DELETE); + } + + // Get all of the entries in the "browser" collection + LLSD browser_history = LLURLHistory::getURLHistory("browser"); + + LLSD::array_iterator iter_history = + browser_history.beginArray(); + LLSD::array_iterator end_history = + browser_history.endArray(); + for(; iter_history != end_history; ++iter_history) + { + std::string url = (*iter_history).asString(); + if(! url.empty()) + url_list->addSimpleElement(url); + } + + // initialize URL history in the plugin + mBrowser->getMediaPlugin()->initializeUrlHistory(browser_history); +} + +std::string LLFloaterMediaBrowser::getSupportURL() +{ + return getString("support_page_url"); +} +void LLFloaterMediaBrowser::onClose(bool app_quitting) +{ + //setVisible(FALSE); + destroy(); +} + +void LLFloaterMediaBrowser::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + if(event == MEDIA_EVENT_LOCATION_CHANGED) + { + setCurrentURL(self->getLocation()); + } + else if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) + { + // This is the event these flags are sent with. + childSetEnabled("back", self->getHistoryBackAvailable()); + childSetEnabled("forward", self->getHistoryForwardAvailable()); + } +} +void LLFloaterMediaBrowser::setCurrentURL(const std::string& url) +{ + mCurrentURL = url; + + // redirects will navigate momentarily to about:blank, don't add to history + if (mCurrentURL != "about:blank") + { + mAddressCombo->remove(mCurrentURL); + mAddressCombo->add(mCurrentURL, ADD_SORTED); + mAddressCombo->selectByValue(mCurrentURL); + + // Serialize url history + LLURLHistory::removeURL("browser", mCurrentURL); + LLURLHistory::addURL("browser", mCurrentURL); + } + childSetEnabled("back", mBrowser->canNavigateBack()); + childSetEnabled("forward", mBrowser->canNavigateForward()); + childSetEnabled("reload", TRUE); +} + +void LLFloaterMediaBrowser::onOpen(const LLSD& media_url) +{ + LLFloater::onOpen(media_url); + openMedia(media_url.asString()); +} + +//static +void LLFloaterMediaBrowser::onEnterAddress(LLUICtrl* ctrl, void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + self->mBrowser->navigateTo(self->mAddressCombo->getValue().asString()); +} + +//static +void LLFloaterMediaBrowser::onClickRefresh(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + self->mAddressCombo->remove(0); + self->mBrowser->navigateTo(self->mCurrentURL); +} + +//static +void LLFloaterMediaBrowser::onClickForward(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + self->mBrowser->navigateForward(); +} + +//static +void LLFloaterMediaBrowser::onClickBack(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + self->mBrowser->navigateBack(); +} + +//static +void LLFloaterMediaBrowser::onClickGo(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + self->mBrowser->navigateTo(self->mAddressCombo->getValue().asString()); +} + +//static +void LLFloaterMediaBrowser::onClickClose(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + self->closeFloater(); +} + +//static +void LLFloaterMediaBrowser::onClickOpenWebBrowser(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + std::string url = self->mCurrentURL.empty() ? + self->mBrowser->getHomePageUrl() : + self->mCurrentURL; + LLWeb::loadURLExternal(url); +} + +void LLFloaterMediaBrowser::onClickAssign(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (!parcel) + { + return; + } + std::string media_url = self->mAddressCombo->getValue().asString(); + LLStringUtil::trim(media_url); + + if(parcel->getMediaType() != "text/html") + { + parcel->setMediaURL(media_url); + parcel->setMediaCurrentURL(media_url); + parcel->setMediaType(std::string("text/html")); + LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel, true ); + LLViewerParcelMedia::sendMediaNavigateMessage(media_url); + LLViewerParcelMedia::stop(); + // LLViewerParcelMedia::update( parcel ); + } + LLViewerParcelMedia::sendMediaNavigateMessage(media_url); +} +//static +void LLFloaterMediaBrowser::onClickRewind(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + if(self->mBrowser->getMediaPlugin()) + self->mBrowser->getMediaPlugin()->start(-2.0f); +} +//static +void LLFloaterMediaBrowser::onClickPlay(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + LLPluginClassMedia* plugin = self->mBrowser->getMediaPlugin(); + if(plugin) + { + if(plugin->getStatus() == LLPluginClassMediaOwner::MEDIA_PLAYING) + { + plugin->pause(); + } + else + { + plugin->start(); + } + } +} +//static +void LLFloaterMediaBrowser::onClickStop(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + if(self->mBrowser->getMediaPlugin()) + self->mBrowser->getMediaPlugin()->stop(); +} +//static +void LLFloaterMediaBrowser::onClickSeek(void* user_data) +{ + LLFloaterMediaBrowser* self = (LLFloaterMediaBrowser*)user_data; + + if(self->mBrowser->getMediaPlugin()) + self->mBrowser->getMediaPlugin()->start(2.0f); +} +void LLFloaterMediaBrowser::openMedia(const std::string& media_url) +{ + mBrowser->setHomePageUrl(media_url); + mBrowser->navigateTo(media_url); + setCurrentURL(media_url); +} +//////////////////////////////////////////////////////////////////////////////// +// + +LLViewerHtmlHelp gViewerHtmlHelp; + + +//////////////////////////////////////////////////////////////////////////////// +// +LLViewerHtmlHelp::LLViewerHtmlHelp() +{ + + LLUI::setHtmlHelp(this); +} + +LLViewerHtmlHelp::~LLViewerHtmlHelp() +{ + + LLUI::setHtmlHelp(NULL); +} + +void LLViewerHtmlHelp::show() +{ + show(""); +} + +void LLViewerHtmlHelp::show(std::string url) +{ + LLFloaterMediaBrowser* floater_html = dynamic_cast<LLFloaterMediaBrowser*>(LLFloaterReg::getInstance("media_browser")); + floater_html->setVisible(FALSE); + + if (url.empty()) + { + url = floater_html->getSupportURL(); + } + + if (gSavedSettings.getBOOL("UseExternalBrowser")) + { + LLSD notificationData; + notificationData["url"] = url; + + LLNotifications::instance().add("ClickOpenF1Help", notificationData, LLSD(), onClickF1HelpLoadURL); + floater_html->closeFloater(); + } + else + { + // don't wait, just do it + floater_html->setVisible(TRUE); + floater_html->openMedia(url); + } +} + +// static +bool LLViewerHtmlHelp::onClickF1HelpLoadURL(const LLSD& notification, const LLSD& response) +{ + LLFloaterMediaBrowser* floater_html = dynamic_cast<LLFloaterMediaBrowser*>(LLFloaterReg::getInstance("media_browser")); + floater_html->setVisible(FALSE); + std::string url = floater_html->getSupportURL(); + S32 option = LLNotification::getSelectedOption(notification, response); + if (option == 0) + { + LLWeb::loadURL(url); + } + floater_html->closeFloater(); + return false; +} + diff --git a/indra/newview/llfloatermediabrowser.h b/indra/newview/llfloatermediabrowser.h new file mode 100644 index 0000000000..76e8b517a0 --- /dev/null +++ b/indra/newview/llfloatermediabrowser.h @@ -0,0 +1,99 @@ +/** + * @file llfloatermediabrowser.h + * @brief HTML Help floater - uses embedded web browser control + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-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_LLFLOATERMEDIABROWSER_H +#define LL_LLFLOATERMEDIABROWSER_H + +#include "llhtmlhelp.h" +#include "llfloater.h" +#include "llmediactrl.h" + +class LLViewerHtmlHelp : public LLHtmlHelp +{ +public: + LLViewerHtmlHelp(); + virtual ~LLViewerHtmlHelp(); + + /*virtual*/ void show(); + /*virtual*/ void show(std::string start_url); + void show(std::string start_url, std::string title); + + static bool onClickF1HelpLoadURL(const LLSD& notification, const LLSD& response); + +}; + +class LLComboBox; +class LLMediaCtrl; + +class LLFloaterMediaBrowser : + public LLFloater, + public LLViewerMediaObserver +{ +public: + LLFloaterMediaBrowser(const LLSD& key); + + /*virtual*/ BOOL postBuild(); + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ void draw(); + /*virtual*/ void onOpen(const LLSD& key); + + // inherited from LLViewerMediaObserver + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + + void openMedia(const std::string& media_url); + void buildURLHistory(); + std::string getSupportURL(); + void setCurrentURL(const std::string& url); + + static void onEnterAddress(LLUICtrl* ctrl, void* user_data); + static void onClickRefresh(void* user_data); + static void onClickBack(void* user_data); + static void onClickForward(void* user_data); + static void onClickGo(void* user_data); + static void onClickClose(void* user_data); + static void onClickOpenWebBrowser(void* user_data); + static void onClickAssign(void* user_data); + static void onClickRewind(void* user_data); + static void onClickPlay(void* user_data); + static void onClickStop(void* user_data); + static void onClickSeek(void* user_data); + +private: + LLMediaCtrl* mBrowser; + LLComboBox* mAddressCombo; + std::string mCurrentURL; +}; + +extern LLViewerHtmlHelp gViewerHtmlHelp; + +#endif // LL_LLFLOATERMEDIABROWSER_H + diff --git a/indra/newview/llfloaternotificationsconsole.cpp b/indra/newview/llfloaternotificationsconsole.cpp index 0a3d97245b..f2dff55044 100644 --- a/indra/newview/llfloaternotificationsconsole.cpp +++ b/indra/newview/llfloaternotificationsconsole.cpp @@ -39,7 +39,6 @@ #include "llscrolllistitem.h" #include "llpanel.h" #include "llcombobox.h" -#include "llviewertexteditor.h" const S32 NOTIFICATION_PANEL_HEADER_HEIGHT = 20; const S32 HEADER_PADDING = 38; @@ -250,7 +249,7 @@ LLFloaterNotification::LLFloaterNotification(LLNotification* note) BOOL LLFloaterNotification::postBuild() { setTitle(mNote->getName()); - getChild<LLViewerTextEditor>("payload")->setText(mNote->getMessage()); + getChild<LLUICtrl>("payload")->setValue(mNote->getMessage()); LLComboBox* responses_combo = getChild<LLComboBox>("response"); LLCtrlListInterface* response_list = responses_combo->getListInterface(); diff --git a/indra/newview/llfloateropenobject.cpp b/indra/newview/llfloateropenobject.cpp index ba23a58b37..aa82dc34b7 100644 --- a/indra/newview/llfloateropenobject.cpp +++ b/indra/newview/llfloateropenobject.cpp @@ -43,7 +43,6 @@ #include "llbutton.h" #include "lltextbox.h" -#include "llagent.h" // for agent id #include "llalertdialog.h" #include "llinventorybridge.h" #include "llfloaterinventory.h" diff --git a/indra/newview/llfloaterparcel.cpp b/indra/newview/llfloaterparcel.cpp index af42ce4f4a..44270683a0 100644 --- a/indra/newview/llfloaterparcel.cpp +++ b/indra/newview/llfloaterparcel.cpp @@ -55,7 +55,7 @@ public: // requires trusted browser to trigger LLParcelHandler() : LLCommandHandler("parcel", true) { } bool handle(const LLSD& params, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { if (params.size() < 2) { diff --git a/indra/newview/llfloaterpostcard.cpp b/indra/newview/llfloaterpostcard.cpp index aa68a1b229..a27070de39 100644 --- a/indra/newview/llfloaterpostcard.cpp +++ b/indra/newview/llfloaterpostcard.cpp @@ -43,8 +43,8 @@ #include "llagent.h" #include "llui.h" #include "lllineeditor.h" -#include "llviewertexteditor.h" #include "llbutton.h" +#include "lltexteditor.h" #include "llfloaterreg.h" #include "llviewercontrol.h" #include "llviewernetwork.h" @@ -64,6 +64,7 @@ #include "llvfs.h" #include "llviewertexture.h" #include "llassetuploadresponders.h" +#include "llagentui.h" #include <boost/regex.hpp> //boost.regex lib @@ -101,17 +102,11 @@ BOOL LLFloaterPostcard::postBuild() childDisable("from_form"); std::string name_string; - gAgent.buildFullname(name_string); + LLAgentUI::buildFullname(name_string); childSetValue("name_form", LLSD(name_string)); - LLTextEditor* MsgField = getChild<LLTextEditor>("msg_form"); - if (MsgField) - { - MsgField->setWordWrap(TRUE); - - // For the first time a user focusess to .the msg box, all text will be selected. - MsgField->setFocusChangedCallback(onMsgFormFocusRecieved, this); - } + // For the first time a user focusess to .the msg box, all text will be selected. + getChild<LLUICtrl>("msg_form")->setFocusChangedCallback(onMsgFormFocusRecieved, this); childSetFocus("to_form", TRUE); diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 6050fbfa5d..b395e1128c 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -62,13 +62,11 @@ #include "llpanellogin.h" #include "llradiogroup.h" #include "llsky.h" -#include "llstylemap.h" #include "llscrolllistctrl.h" #include "llscrolllistitem.h" #include "llsliderctrl.h" #include "lltabcontainer.h" #include "lltrans.h" -#include "lltexteditor.h" #include "llviewercontrol.h" #include "llviewercamera.h" #include "llviewerwindow.h" @@ -101,6 +99,8 @@ #include "pipeline.h" #include "lluictrlfactory.h" #include "llboost.h" +#include "llviewermedia.h" +#include "llpluginclassmedia.h" //RN temporary includes for resolution switching @@ -168,10 +168,8 @@ void LLVoiceSetKeyDialog::onCancel(void* user_data) // if creating/destroying these is too slow, we'll need to create // a static member and update all our static callbacks -void free_web_media(LLMediaBase *media_source); -void handleHTMLLinkColorChanged(const LLSD& newvalue); void handleNameTagOptionChanged(const LLSD& newvalue); -LLMediaBase *get_web_media(); +viewer_media_t get_web_media(); bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response); bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); @@ -180,42 +178,13 @@ bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFl bool extractWindowSizeFromString(const std::string& instr, U32 &width, U32 &height); void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator); -LLMediaBase *get_web_media() +viewer_media_t get_web_media() { - LLMediaBase *media_source; - LLMediaManager *mgr = LLMediaManager::getInstance(); - - if (!mgr) - { - llwarns << "cannot get media manager" << llendl; - return NULL; - } - - media_source = mgr->createSourceFromMimeType("http", "text/html" ); - if ( !media_source ) - { - llwarns << "media source create failed " << llendl; - return NULL; - } + viewer_media_t media_source = LLViewerMedia::newMediaImpl("", LLUUID::null, 0, 0, 0, 0, "text/html"); return media_source; } -void free_web_media(LLMediaBase *media_source) -{ - if (!media_source) - return; - - LLMediaManager *mgr = LLMediaManager::getInstance(); - if (!mgr) - { - llwarns << "cannot get media manager" << llendl; - return; - } - - mgr->destroySource(media_source); -} - bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response) { @@ -223,10 +192,9 @@ bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response if ( option == 0 ) // YES { // clean web - LLMediaBase *media_source = get_web_media(); - if (media_source) - media_source->clearCache(); - free_web_media(media_source); + viewer_media_t media_source = get_web_media(); + if (media_source && media_source->hasMedia()) + media_source->getMediaPlugin()->clear_cache(); // clean nav bar history LLNavigationBar::getInstance()->clearHistoryCache(); @@ -239,12 +207,6 @@ bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response return false; } -void handleHTMLLinkColorChanged(const LLSD& newvalue) -{ - LLTextEditor::setLinkColor(LLColor4(newvalue)); - LLStyleMap::instance().update(); - -} void handleNameTagOptionChanged(const LLSD& newvalue) { S32 name_tag_option = S32(newvalue); @@ -422,7 +384,7 @@ void LLFloaterPreference::apply() panel->apply(); } // hardware menu apply - LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::findTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings"); + LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::getTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings"); if (hardware_settings) { hardware_settings->apply(); @@ -443,29 +405,25 @@ void LLFloaterPreference::apply() std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); childSetText("cache_location", cache_location); - LLMediaBase *media_source = get_web_media(); - if (media_source) + viewer_media_t media_source = get_web_media(); + if (media_source && media_source->hasMedia()) { - media_source->enableCookies(childGetValue("cookies_enabled")); + media_source->getMediaPlugin()->enable_cookies(childGetValue("cookies_enabled")); if(hasChild("web_proxy_enabled") &&hasChild("web_proxy_editor") && hasChild("web_proxy_port")) { bool proxy_enable = childGetValue("web_proxy_enabled"); std::string proxy_address = childGetValue("web_proxy_editor"); - int proxy_port = childGetValue("web_proxy_port"); - media_source->enableProxy(proxy_enable, proxy_address, proxy_port); + media_source->getMediaPlugin()->proxy_setup(proxy_enable, proxy_address, proxy_port); } } - free_web_media(media_source); - LLTextEditor* busy = getChild<LLTextEditor>("busy_response"); - LLWString busy_response; - if (busy) busy_response = busy->getWText(); - LLWStringUtil::replaceTabsWithSpaces(busy_response, 4); +// LLWString busy_response = utf8str_to_wstring(getChild<LLUICtrl>("busy_response")->getValue().asString()); +// LLWStringUtil::replaceTabsWithSpaces(busy_response, 4); if(mGotPersonalInfo) { - gSavedPerAccountSettings.setString("BusyModeResponse2", std::string(wstring_to_utf8str(busy_response))); +// gSavedSettings.setString("BusyModeResponse2", std::string(wstring_to_utf8str(busy_response))); bool new_im_via_email = childGetValue("send_im_to_email").asBoolean(); bool new_hide_online = childGetValue("online_visibility").asBoolean(); @@ -514,7 +472,7 @@ void LLFloaterPreference::cancel() LLFloaterReg::hideInstance("pref_joystick"); // cancel hardware menu - LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::findTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings"); + LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::getTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings"); if (hardware_settings) { hardware_settings->cancel(); @@ -569,7 +527,7 @@ void LLFloaterPreference::onBtnOK() // commit any outstanding text entry if (hasFocus()) { - LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus(); + LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus()); if (cur_focus->acceptsTextInput()) { cur_focus->onCommit(); @@ -606,7 +564,7 @@ void LLFloaterPreference::onBtnApply( ) { if (hasFocus()) { - LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus(); + LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus()); if (cur_focus->acceptsTextInput()) { cur_focus->onCommit(); @@ -622,7 +580,7 @@ void LLFloaterPreference::onBtnCancel() { if (hasFocus()) { - LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus(); + LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus()); if (cur_focus->acceptsTextInput()) { cur_focus->onCommit(); @@ -649,7 +607,7 @@ void LLFloaterPreference::refreshEnabledGraphics() { instance->refreshEnabledState(); } - LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::findTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings"); + LLFloaterHardwareSettings* hardware_settings = LLFloaterReg::getTypedInstance<LLFloaterHardwareSettings>("prefs_hardware_settings"); if (hardware_settings) { hardware_settings->refreshEnabledState(); @@ -1172,13 +1130,13 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility, bool im childSetValue("send_im_to_email", im_via_email); childEnable("log_instant_messages"); // childEnable("log_chat"); - childEnable("busy_response"); +// childEnable("busy_response"); // childEnable("log_instant_messages_timestamp"); // childEnable("log_chat_timestamp"); childEnable("log_chat_IM"); childEnable("log_date_timestamp"); - childSetText("busy_response", gSavedPerAccountSettings.getString("BusyModeResponse2")); +// childSetText("busy_response", gSavedSettings.getString("BusyModeResponse2")); enableHistory(); std::string display_email(email); @@ -1349,8 +1307,7 @@ static LLRegisterPanelClassWrapper<LLPanelPreference> t_places("panel_preference LLPanelPreference::LLPanelPreference() : LLPanel() { - // - mCommitCallbackRegistrar.add("setControlFalse", boost::bind(&LLPanelPreference::setControlFalse,this, _2)); + mCommitCallbackRegistrar.add("Pref.setControlFalse", boost::bind(&LLPanelPreference::setControlFalse,this, _2)); } static void applyUIColor(const std::string& color_name, LLUICtrl* ctrl, const LLSD& param) @@ -1422,23 +1379,17 @@ BOOL LLPanelPreference::postBuild() ////// if(hasChild("online_visibility") && hasChild("send_im_to_email")) { - requires("online_visibility"); - requires("send_im_to_email"); - if (!checkRequirements()) - { - return FALSE; - } childSetText("email_address",getString("log_in_to_change") ); - childSetText("busy_response", getString("log_in_to_change")); +// childSetText("busy_response", getString("log_in_to_change")); } - if(hasChild("fullscreen combo")) + if(hasChild("aspect_ratio")) { //============================================================================ // Resolution - +/* S32 num_resolutions = 0; LLWindow::LLWindowResolution* supported_resolutions = gViewerWindow->getWindow()->getSupportedResolutions(num_resolutions); @@ -1481,7 +1432,7 @@ BOOL LLPanelPreference::postBuild() ctrl_full_screen->setCurrentByIndex(0); } } - + */ LLFloaterPreference::initWindowSizeControls(this); if (gSavedSettings.getBOOL("FullScreenAutoDetectAspectRatio")) diff --git a/indra/newview/llfloaterproperties.cpp b/indra/newview/llfloaterproperties.cpp index 8ac00832c9..ab281e9f9f 100644 --- a/indra/newview/llfloaterproperties.cpp +++ b/indra/newview/llfloaterproperties.cpp @@ -46,6 +46,7 @@ #include "llavataractions.h" #include "llinventorymodel.h" #include "lllineeditor.h" +#include "llspinctrl.h" #include "llradiogroup.h" #include "llresmgr.h" #include "roles_constants.h" @@ -151,7 +152,7 @@ BOOL LLFloaterProperties::postBuild() getChild<LLUICtrl>("CheckPurchase")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this)); getChild<LLUICtrl>("RadioSaleType")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleType, this)); // "Price" label for edit - getChild<LLUICtrl>("EditPrice")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this)); + getChild<LLUICtrl>("Edit Cost")->setCommitCallback(boost::bind(&LLFloaterProperties::onCommitSaleInfo, this)); // The UI has been built, now fill in all the values refresh(); @@ -195,7 +196,7 @@ void LLFloaterProperties::refresh() "CheckNextOwnerTransfer", "CheckPurchase", "RadioSaleType", - "EditPrice" + "Edit Cost" }; for(size_t t=0; t<LL_ARRAY_SIZE(enableNames); ++t) { @@ -499,7 +500,7 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) childSetEnabled("RadioSaleType",is_complete && is_for_sale); childSetEnabled("TextPrice",is_complete && is_for_sale); - childSetEnabled("EditPrice",is_complete && is_for_sale); + childSetEnabled("Edit Cost",is_complete && is_for_sale); } else { @@ -513,11 +514,13 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) childSetEnabled("RadioSaleType",FALSE); childSetEnabled("TextPrice",FALSE); - childSetEnabled("EditPrice",FALSE); + childSetEnabled("Edit Cost",FALSE); } // Set values. childSetValue("CheckPurchase", is_for_sale); + childSetEnabled("combobox sale copy", is_for_sale); + childSetEnabled("Edit Cost", is_for_sale); childSetValue("CheckNextOwnerModify",LLSD(BOOL(next_owner_mask & PERM_MODIFY))); childSetValue("CheckNextOwnerCopy",LLSD(BOOL(next_owner_mask & PERM_COPY))); childSetValue("CheckNextOwnerTransfer",LLSD(BOOL(next_owner_mask & PERM_TRANSFER))); @@ -528,12 +531,12 @@ void LLFloaterProperties::refreshFromItem(LLInventoryItem* item) radioSaleType->setSelectedIndex((S32)sale_info.getSaleType() - 1); S32 numerical_price; numerical_price = sale_info.getSalePrice(); - childSetText("EditPrice",llformat("%d",numerical_price)); + childSetText("Edit Cost",llformat("%d",numerical_price)); } else { radioSaleType->setSelectedIndex(-1); - childSetText("EditPrice",llformat("%d",0)); + childSetText("Edit Cost",llformat("%d",0)); } } @@ -790,12 +793,13 @@ void LLFloaterProperties::updateSaleInfo() sale_type = LLSaleInfo::FS_ORIGINAL; } - LLLineEditor* EditPrice = getChild<LLLineEditor>("EditPrice"); + // LLLineEditor* EditPrice = getChild<LLLineEditor>("EditPrice"); + LLSpinCtrl *EditPrice = getChild<LLSpinCtrl>("Edit Cost"); S32 price = -1; if(EditPrice) { - price = atoi(EditPrice->getText().c_str()); + price = EditPrice->getValue().asInteger();; } // Invalid data - turn off the sale if (price < 0) diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp index 94d25aa0c8..10276ba36d 100644 --- a/indra/newview/llfloaterregioninfo.cpp +++ b/indra/newview/llfloaterregioninfo.cpp @@ -84,6 +84,7 @@ #include "llviewerwindow.h" #include "llvlcomposition.h" #include "lltrans.h" +#include "llagentui.h" #define ELAR_ENABLED 0 // Enable when server support is implemented @@ -714,7 +715,7 @@ bool LLPanelRegionGeneralInfo::onMessageCommit(const LLSD& notification, const L gAgent.getID().toString(buffer); strings.push_back(buffer); std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); strings.push_back(strings_t::value_type(name)); strings.push_back(strings_t::value_type(text)); LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); @@ -2659,7 +2660,7 @@ bool LLPanelEstateInfo::onMessageCommit(const LLSD& notification, const LLSD& re strings_t strings; //integers_t integers; std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); strings.push_back(strings_t::value_type(name)); strings.push_back(strings_t::value_type(text)); LLUUID invoice(LLFloaterRegionInfo::getLastInvoice()); @@ -2746,7 +2747,6 @@ BOOL LLPanelEstateCovenant::postBuild() mEstateOwnerText = getChild<LLTextBox>("estate_owner_text"); mLastModifiedText = getChild<LLTextBox>("covenant_timestamp_text"); mEditor = getChild<LLViewerTextEditor>("covenant_editor"); - if (mEditor) mEditor->setHandleEditKeysDirectly(TRUE); LLButton* reset_button = getChild<LLButton>("reset_covenant"); reset_button->setEnabled(gAgent.canManageEstate()); reset_button->setClickedCallback(LLPanelEstateCovenant::resetCovenantID, NULL); diff --git a/indra/newview/llfloaterreporter.cpp b/indra/newview/llfloaterreporter.cpp index 00cbc2f0c8..818381b561 100644 --- a/indra/newview/llfloaterreporter.cpp +++ b/indra/newview/llfloaterreporter.cpp @@ -48,6 +48,7 @@ #include "llversionviewer.h" #include "message.h" #include "v3math.h" +#include "lltexteditor.h" // viewer project includes #include "llagent.h" @@ -61,7 +62,6 @@ #include "llimview.h" #include "lltextbox.h" #include "lldispatcher.h" -#include "llviewertexteditor.h" #include "llviewerobject.h" #include "llviewerregion.h" #include "llcombobox.h" @@ -85,6 +85,7 @@ #include "llviewernetwork.h" #include "llassetuploadresponders.h" +#include "llagentui.h" const U32 INCLUDE_SCREENSHOT = 0x01 << 0; @@ -137,7 +138,7 @@ void LLFloaterReporter::processRegionInfo(LLMessageSystem* msg) // virtual BOOL LLFloaterReporter::postBuild() { - childSetText("abuse_location_edit", gAgent.getSLURL() ); + childSetText("abuse_location_edit", LLAgentUI::buildSLURL()); enableControls(TRUE); @@ -190,7 +191,7 @@ BOOL LLFloaterReporter::postBuild() // grab the user's name std::string fullname; - gAgent.buildFullname(fullname); + LLAgentUI::buildFullname(fullname); childSetText("reporter_field", fullname); center(); @@ -499,7 +500,7 @@ void LLFloaterReporter::showFromObject(const LLUUID& object_id) // grab the user's name std::string fullname; - gAgent.buildFullname(fullname); + LLAgentUI::buildFullname(fullname); f->childSetText("reporter_field", fullname); // Request info for this object diff --git a/indra/newview/llfloaterscriptdebug.cpp b/indra/newview/llfloaterscriptdebug.cpp index c8690de68c..328fb6450e 100644 --- a/indra/newview/llfloaterscriptdebug.cpp +++ b/indra/newview/llfloaterscriptdebug.cpp @@ -80,9 +80,6 @@ BOOL LLFloaterScriptDebug::postBuild() if (mTabContainer) { - // *FIX: apparantly fails for tab containers? -// mTabContainer->requires<LLFloater>("all_scripts"); -// mTabContainer->checkRequirements(); return TRUE; } diff --git a/indra/newview/llfloatersettingsdebug.cpp b/indra/newview/llfloatersettingsdebug.cpp index 53b40f8b7a..616e9bac35 100644 --- a/indra/newview/llfloatersettingsdebug.cpp +++ b/indra/newview/llfloatersettingsdebug.cpp @@ -39,6 +39,7 @@ #include "llspinctrl.h" #include "llcolorswatch.h" #include "llviewercontrol.h" +#include "lltexteditor.h" LLFloaterSettingsDebug::LLFloaterSettingsDebug(const LLSD& key) diff --git a/indra/newview/llfloatersettingsdebug.h b/indra/newview/llfloatersettingsdebug.h index e7dda3a5f4..65803fbf70 100644 --- a/indra/newview/llfloatersettingsdebug.h +++ b/indra/newview/llfloatersettingsdebug.h @@ -35,7 +35,6 @@ #include "llcontrol.h" #include "llfloater.h" -#include "lltexteditor.h" class LLFloaterSettingsDebug : public LLFloater @@ -60,7 +59,7 @@ private: virtual ~LLFloaterSettingsDebug(); protected: - LLTextEditor* mComment; + class LLTextEditor* mComment; }; #endif //LLFLOATERDEBUGSETTINGS_H diff --git a/indra/newview/llfloatersnapshot.cpp b/indra/newview/llfloatersnapshot.cpp index 371dfa50cd..dd73ebbd8f 100644 --- a/indra/newview/llfloatersnapshot.cpp +++ b/indra/newview/llfloatersnapshot.cpp @@ -41,7 +41,6 @@ #include "llcallbacklist.h" #include "llcriticaldamp.h" #include "llui.h" -#include "llviewertexteditor.h" #include "llfocusmgr.h" #include "llbutton.h" #include "llcombobox.h" @@ -60,6 +59,7 @@ #include "lltoolfocus.h" #include "lltoolmgr.h" #include "llworld.h" +#include "llagentui.h" // Linden library includes #include "llfontgl.h" @@ -968,9 +968,9 @@ void LLSnapshotLivePreview::saveTexture() { LLVFile::writeFile(formatted->getData(), formatted->getDataSize(), gVFS, new_asset_id, LLAssetType::AT_TEXTURE); std::string pos_string; - gAgent.buildLocationString(pos_string, LLAgent::LOCATION_FORMAT_FULL); + LLAgentUI::buildLocationString(pos_string, LLAgent::LOCATION_FORMAT_FULL); std::string who_took_it; - gAgent.buildFullname(who_took_it); + LLAgentUI::buildFullname(who_took_it); LLAssetStorage::LLStoreAssetCallback callback = NULL; S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); void *userdata = NULL; diff --git a/indra/newview/llfloatertos.cpp b/indra/newview/llfloatertos.cpp index e43c3ecde7..d9bc43f3b4 100644 --- a/indra/newview/llfloatertos.cpp +++ b/indra/newview/llfloatertos.cpp @@ -35,9 +35,7 @@ #include "llfloatertos.h" // viewer includes -#include "llagent.h" #include "llviewerstats.h" -#include "llviewertexteditor.h" #include "llviewerwindow.h" // linden library includes @@ -117,12 +115,10 @@ BOOL LLFloaterTOS::postBuild() if (hasChild("tos_text")) { // this displays the critical message - LLTextEditor *editor = getChild<LLTextEditor>("tos_text"); - editor->setHandleEditKeysDirectly( TRUE ); - editor->setEnabled( FALSE ); - editor->setWordWrap(TRUE); - editor->setFocus(TRUE); - editor->setValue(LLSD(mMessage)); + LLUICtrl *tos_text = getChild<LLUICtrl>("tos_text"); + tos_text->setEnabled( FALSE ); + tos_text->setFocus(TRUE); + tos_text->setValue(LLSD(mMessage)); return TRUE; } @@ -132,15 +128,13 @@ BOOL LLFloaterTOS::postBuild() tos_agreement->setEnabled( false ); // hide the SL text widget if we're displaying TOS with using a browser widget. - LLTextEditor *editor = getChild<LLTextEditor>("tos_text"); + LLUICtrl *editor = getChild<LLUICtrl>("tos_text"); editor->setVisible( FALSE ); - LLWebBrowserCtrl* web_browser = getChild<LLWebBrowserCtrl>("tos_html"); + LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("tos_html"); if ( web_browser ) { - // start to observe it so we see navigate complete events - web_browser->addObserver( this ); - + web_browser->addObserver(this); gResponsePtr = LLIamHere::build( this ); LLHTTPClient::get( getString( "real_url" ), gResponsePtr ); } @@ -153,7 +147,7 @@ void LLFloaterTOS::setSiteIsAlive( bool alive ) // only do this for TOS pages if (hasChild("tos_html")) { - LLWebBrowserCtrl* web_browser = getChild<LLWebBrowserCtrl>("tos_html"); + LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("tos_html"); // if the contents of the site was retrieved if ( alive ) { @@ -172,12 +166,6 @@ void LLFloaterTOS::setSiteIsAlive( bool alive ) LLFloaterTOS::~LLFloaterTOS() { - // stop obsaerving events - LLWebBrowserCtrl* web_browser = getChild<LLWebBrowserCtrl>("tos_html"); - if ( web_browser ) - { - web_browser->remObserver( this ); - }; // tell the responder we're not here anymore if ( gResponsePtr ) @@ -229,15 +217,18 @@ void LLFloaterTOS::onCancel( void* userdata ) } //virtual -void LLFloaterTOS::onNavigateComplete( const EventType& eventIn ) +void LLFloaterTOS::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent event) { - // skip past the loading screen navigate complete - if ( ++mLoadCompleteCount == 2 ) + if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) { - llinfos << "NAVIGATE COMPLETE" << llendl; - // enable Agree to TOS radio button now that page has loaded - LLCheckBoxCtrl * tos_agreement = getChild<LLCheckBoxCtrl>("agree_chk"); - tos_agreement->setEnabled( true ); - }; + // skip past the loading screen navigate complete + if ( ++mLoadCompleteCount == 2 ) + { + llinfos << "NAVIGATE COMPLETE" << llendl; + // enable Agree to TOS radio button now that page has loaded + LLCheckBoxCtrl * tos_agreement = getChild<LLCheckBoxCtrl>("agree_chk"); + tos_agreement->setEnabled( true ); + } + } } diff --git a/indra/newview/llfloatertos.h b/indra/newview/llfloatertos.h index 89ad29170c..1d573e8170 100644 --- a/indra/newview/llfloatertos.h +++ b/indra/newview/llfloatertos.h @@ -35,7 +35,7 @@ #include "llmodaldialog.h" #include "llassetstorage.h" -#include "llwebbrowserctrl.h" +#include "llmediactrl.h" #include <boost/function.hpp> class LLButton; @@ -46,7 +46,7 @@ class LLUUID; class LLFloaterTOS : public LLModalDialog, - public LLWebBrowserCtrlObserver + public LLViewerMediaObserver { public: LLFloaterTOS(const LLSD& data); @@ -62,7 +62,8 @@ public: void setSiteIsAlive( bool alive ); - virtual void onNavigateComplete( const EventType& eventIn ); + // inherited from LLViewerMediaObserver + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); private: std::string mMessage; diff --git a/indra/newview/llfloateruipreview.cpp b/indra/newview/llfloateruipreview.cpp index e5b4657742..2eb4e7580e 100644 --- a/indra/newview/llfloateruipreview.cpp +++ b/indra/newview/llfloateruipreview.cpp @@ -747,8 +747,8 @@ void LLFloaterUIPreview::displayFloater(BOOL click, S32 ID, bool save) panel->setUseBoundingRect(TRUE); // enable the use of its outer bounding rect (normally disabled because it's O(n) on the number of sub-elements) panel->updateBoundingRect(); // update bounding rect LLRect bounding_rect = panel->getBoundingRect(); // get the bounding rect - LLRect panel_rect = panel->getRect(); // get the panel's rect - LLRect new_rect = panel_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible + LLRect new_rect = panel->getRect(); // get the panel's rect + new_rect.unionWith(bounding_rect); // union them to make sure we get the biggest one possible (*floaterp)->reshape(new_rect.getWidth(), new_rect.getHeight() + floater_header_size); // reshape floater to match the union rect's dimensions panel->reshape(new_rect.getWidth(), new_rect.getHeight()); // reshape panel to match the union rect's dimensions as well (both are needed) (*floaterp)->addChild(panel); // add panel as child @@ -1274,7 +1274,7 @@ void LLFloaterUIPreview::highlightChangedElements() BOOL failed = FALSE; for(token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) { - element = element->getChild<LLView>(*token_iter,FALSE,FALSE); // try to find element: don't recur, and don't create if missing + element = element->findChild<LLView>(*token_iter,FALSE); // try to find element: don't recur, and don't create if missing // if we still didn't find it... if(NULL == element) diff --git a/indra/newview/llfloaterurlentry.cpp b/indra/newview/llfloaterurlentry.cpp index 2ad41291f3..1e975cd447 100644 --- a/indra/newview/llfloaterurlentry.cpp +++ b/indra/newview/llfloaterurlentry.cpp @@ -227,7 +227,7 @@ void LLFloaterURLEntry::onBtnOK( void* userdata ) } // Discover the MIME type only for "http" scheme. - if(scheme == "http") + if(scheme == "http" || scheme == "https") { LLHTTPClient::getHeaderOnly( media_url, new LLMediaTypeResponder(self->getHandle())); diff --git a/indra/newview/llfloatervoicedevicesettings.cpp b/indra/newview/llfloatervoicedevicesettings.cpp index fa76bb728d..a7658d90e9 100644 --- a/indra/newview/llfloatervoicedevicesettings.cpp +++ b/indra/newview/llfloatervoicedevicesettings.cpp @@ -36,7 +36,6 @@ #include "llfloatervoicedevicesettings.h" // Viewer includes -#include "llagent.h" #include "llbutton.h" #include "llcombobox.h" #include "llfocusmgr.h" @@ -311,6 +310,7 @@ void LLFloaterVoiceDeviceSettings::onClose() { if(mDevicePanel) { + mDevicePanel->apply(); mDevicePanel->cleanup(); } } diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp index 4a5a775a05..2a29566120 100644 --- a/indra/newview/llfolderview.cpp +++ b/indra/newview/llfolderview.cpp @@ -34,7 +34,6 @@ #include "llfolderview.h" -#include "llagent.h" #include "llcallbacklist.h" #include "llinventorybridge.h" #include "llinventoryclipboard.h" // *TODO: remove this once hack below gone. @@ -176,7 +175,7 @@ LLFolderView::LLFolderView(const Params& p) mSourceID(p.task_id), mRenameItem( NULL ), mNeedsScroll( FALSE ), - mLastScrollItem( NULL ), + mPinningSelectedItem(FALSE), mNeedsAutoSelect( FALSE ), mAutoSelectOverride(FALSE), mNeedsAutoRename(FALSE), @@ -191,7 +190,6 @@ LLFolderView::LLFolderView(const Params& p) mDragAndDropThisFrame(FALSE), mCallbackRegistrar(NULL), mParentPanel(p.parent_panel) - { LLRect rect = p.rect; LLRect new_rect(rect.mLeft, rect.mBottom + getRect().getHeight(), rect.mLeft + getRect().getWidth(), rect.mBottom); @@ -290,11 +288,13 @@ void LLFolderView::checkTreeResortForModelChanged() } } +static LLFastTimer::DeclareTimer FTM_SORT("Sort Inventory"); + void LLFolderView::setSortOrder(U32 order) { if (order != mSortOrder) { - LLFastTimer t(LLFastTimer::FTM_SORT); + LLFastTimer t(FTM_SORT); mSortOrder = order; for (folders_t::iterator iter = mFolders.begin(); @@ -359,10 +359,12 @@ void LLFolderView::setOpenArrangeRecursively(BOOL openitem, ERecurseType recurse mIsOpen = TRUE; } +static LLFastTimer::DeclareTimer FTM_ARRANGE("Arrange"); + // This view grows and shinks to enclose all of its children items and folders. S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_generation ) { - LLFastTimer t2(LLFastTimer::FTM_ARRANGE); + LLFastTimer t2(FTM_ARRANGE); filter_generation = mFilter->getMinRequiredGeneration(); mMinWidth = 0; @@ -431,17 +433,13 @@ S32 LLFolderView::arrange( S32* unused_width, S32* unused_height, S32 filter_gen } } - S32 dummy_s32; - BOOL dummy_bool; - S32 min_width; - mScrollContainer->calcVisibleSize( &min_width, &dummy_s32, &dummy_bool, &dummy_bool); - reshape( llmax(min_width, total_width), running_height ); + LLRect scroll_rect = mScrollContainer->getContentWindowRect(); + reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); - S32 new_min_width; - mScrollContainer->calcVisibleSize( &new_min_width, &dummy_s32, &dummy_bool, &dummy_bool); - if (new_min_width != min_width) + LLRect new_scroll_rect = mScrollContainer->getContentWindowRect(); + if (new_scroll_rect.getWidth() != scroll_rect.getWidth()) { - reshape( llmax(min_width, total_width), running_height ); + reshape( llmax(scroll_rect.getWidth(), total_width), running_height ); } // move item renamer text field to item's new position @@ -456,9 +454,11 @@ const std::string LLFolderView::getFilterSubString(BOOL trim) return mFilter->getFilterSubString(trim); } +static LLFastTimer::DeclareTimer FTM_FILTER("Filter Inventory"); + void LLFolderView::filter( LLInventoryFilter& filter ) { - LLFastTimer t2(LLFastTimer::FTM_FILTER); + LLFastTimer t2(FTM_FILTER); filter.setFilterCount(llclamp(gSavedSettings.getS32("FilterItemsPerFrame"), 1, 5000)); if (getCompletedFilterGeneration() < filter.getCurrentGeneration()) @@ -467,18 +467,20 @@ void LLFolderView::filter( LLInventoryFilter& filter ) mMinWidth = 0; LLFolderViewFolder::filter(filter); } + else + { + mFiltered = TRUE; + } } void LLFolderView::reshape(S32 width, S32 height, BOOL called_from_parent) { - S32 min_width = 0; - S32 dummy_height; - BOOL dummy_bool; + LLRect scroll_rect; if (mScrollContainer) { - mScrollContainer->calcVisibleSize( &min_width, &dummy_height, &dummy_bool, &dummy_bool); + scroll_rect = mScrollContainer->getContentWindowRect(); } - width = llmax(mMinWidth, min_width); + width = llmax(mMinWidth, scroll_rect.getWidth()); LLView::reshape(width, height, called_from_parent); mReshapeSignal(mSelectedItems, FALSE); @@ -1127,7 +1129,9 @@ void LLFolderView::autoOpenItem( LLFolderViewFolder* item ) mAutoOpenItems.push(item); item->setOpen(TRUE); - scrollToShowItem(item); + LLRect content_rect = mScrollContainer->getContentWindowRect(); + LLRect constraint_rect(0,content_rect.getHeight(), content_rect.getWidth(), 0); + scrollToShowItem(item, constraint_rect); } void LLFolderView::closeAutoOpenedFolders() @@ -1791,49 +1795,41 @@ void LLFolderView::scrollToShowSelection() // If the parent is scroll containter, scroll it to make the selection // is maximally visible. -void LLFolderView::scrollToShowItem(LLFolderViewItem* item) +void LLFolderView::scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect) { + if (!mScrollContainer) return; + // don't scroll to items when mouse is being used to scroll/drag and drop if (gFocusMgr.childHasMouseCapture(mScrollContainer)) { mNeedsScroll = FALSE; return; } - if(item && mScrollContainer) + + // if item exists and is in visible portion of parent folder... + if(item) { - LLRect local_rect = item->getRect(); + LLRect local_rect = item->getLocalRect(); LLRect item_scrolled_rect; // item position relative to display area of scroller + LLRect visible_doc_rect = mScrollContainer->getVisibleContentRect(); S32 icon_height = mIcon.isNull() ? 0 : mIcon->getHeight(); S32 label_height = llround(sFont->getLineHeight()); // when navigating with keyboard, only move top of folders on screen, otherwise show whole folder - S32 max_height_to_show = gFocusMgr.childHasKeyboardFocus(this) ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); - item->localPointToOtherView(item->getIndentation(), llmax(0, local_rect.getHeight() - max_height_to_show), &item_scrolled_rect.mLeft, &item_scrolled_rect.mBottom, mScrollContainer); - item->localPointToOtherView(local_rect.getWidth(), local_rect.getHeight(), &item_scrolled_rect.mRight, &item_scrolled_rect.mTop, mScrollContainer); - - item_scrolled_rect.mRight = llmin(item_scrolled_rect.mLeft + MIN_ITEM_WIDTH_VISIBLE, item_scrolled_rect.mRight); - LLCoordGL scroll_offset(-mScrollContainer->getBorderWidth() - item_scrolled_rect.mLeft, - mScrollContainer->getRect().getHeight() - item_scrolled_rect.mTop - 1); - - S32 max_scroll_offset = getVisibleRect().getHeight() - item_scrolled_rect.getHeight(); - if (item != mLastScrollItem || // if we're scrolling to focus on a new item - // or the item has just appeared on screen and it wasn't onscreen before - (scroll_offset.mY > 0 && scroll_offset.mY < max_scroll_offset && - (mLastScrollOffset.mY < 0 || mLastScrollOffset.mY > max_scroll_offset))) - { - // we now have a position on screen that we want to keep stable - // offset of selection relative to top of visible area - mLastScrollOffset = scroll_offset; - mLastScrollItem = item; - } + S32 max_height_to_show = mScrollContainer->hasFocus() ? (llmax( icon_height, label_height ) + ICON_PAD) : local_rect.getHeight(); + + // get portion of item that we want to see... + LLRect item_local_rect = LLRect(item->getIndentation(), + local_rect.getHeight(), + llmin(MIN_ITEM_WIDTH_VISIBLE, local_rect.getWidth()), + llmax(0, local_rect.getHeight() - max_height_to_show)); + + LLRect item_doc_rect; + + item->localRectToOtherView(item_local_rect, &item_doc_rect, this); - mScrollContainer->scrollToShowRect( item_scrolled_rect, mLastScrollOffset ); + mScrollContainer->scrollToShowRect( item_doc_rect, constraint_rect ); - // after scrolling, store new offset - // in case we don't have room to maintain the original position - LLCoordGL new_item_left_top; - item->localPointToOtherView(item->getIndentation(), item->getRect().getHeight(), &new_item_left_top.mX, &new_item_left_top.mY, mScrollContainer); - mLastScrollOffset.set(-mScrollContainer->getBorderWidth() - new_item_left_top.mX, mScrollContainer->getRect().getHeight() - new_item_left_top.mY - 1); } } @@ -1973,10 +1969,13 @@ bool LLFolderView::doToSelected(LLInventoryModel* model, const LLSD& userdata) return true; } +static LLFastTimer::DeclareTimer FTM_AUTO_SELECT("Open and Select"); +static LLFastTimer::DeclareTimer FTM_INVENTORY("Inventory"); + // Main idle routine void LLFolderView::doIdle() { - LLFastTimer t2(LLFastTimer::FTM_INVENTORY); + LLFastTimer t2(FTM_INVENTORY); BOOL debug_filters = gSavedSettings.getBOOL("DebugInventoryFilters"); if (debug_filters != getDebugFilters()) @@ -1990,7 +1989,7 @@ void LLFolderView::doIdle() mFilter->isNotDefault(); mNeedsAutoSelect = filter_modified_and_active && !(gFocusMgr.childHasKeyboardFocus(this) || gFocusMgr.getMouseCapture()); - + // filter to determine visiblity before arranging filterFromRoot(); @@ -2001,7 +2000,7 @@ void LLFolderView::doIdle() // potentially changed if (mNeedsAutoSelect) { - LLFastTimer t3(LLFastTimer::FTM_AUTO_SELECT); + LLFastTimer t3(FTM_AUTO_SELECT); // select new item only if a filtered item not currently selected LLFolderViewItem* selected_itemp = mSelectedItems.empty() ? NULL : mSelectedItems.back(); if ((!selected_itemp || !selected_itemp->getFiltered()) && !mAutoSelectOverride) @@ -2013,6 +2012,59 @@ void LLFolderView::doIdle() scrollToShowSelection(); } + // during filtering process, try to pin selected item's location on screen + // this will happen when searching your inventory and when new items arrive + if (filter_modified_and_active) + { + // calculate rectangle to pin item to at start of animated rearrange + if (!mPinningSelectedItem && !mSelectedItems.empty()) + { + // lets pin it! + mPinningSelectedItem = TRUE; + + LLRect visible_content_rect = mScrollContainer->getVisibleContentRect(); + LLFolderViewItem* selected_item = mSelectedItems.back(); + + LLRect item_rect; + selected_item->localRectToOtherView(selected_item->getLocalRect(), &item_rect, this); + // if item is visible in scrolled region + if (visible_content_rect.overlaps(item_rect)) + { + // then attempt to keep it in same place on screen + mScrollConstraintRect = item_rect; + mScrollConstraintRect.translate(-visible_content_rect.mLeft, -visible_content_rect.mBottom); + } + else + { + // otherwise we just want it onscreen somewhere + LLRect content_rect = mScrollContainer->getContentWindowRect(); + mScrollConstraintRect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + } + } + } + else + { + // stop pinning selected item after folders stop rearranging + if (!needsArrange()) + { + mPinningSelectedItem = FALSE; + } + } + + LLRect constraint_rect; + if (mPinningSelectedItem) + { + // use last known constraint rect for pinned item + constraint_rect = mScrollConstraintRect; + } + else + { + // during normal use (page up/page down, etc), just try to fit item on screen + LLRect content_rect = mScrollContainer->getContentWindowRect(); + constraint_rect.setOriginAndSize(0, 0, content_rect.getWidth(), content_rect.getHeight()); + } + + BOOL is_visible = isInVisibleChain(); if ( is_visible ) @@ -2026,10 +2078,10 @@ void LLFolderView::doIdle() if (mSelectedItems.size() && mNeedsScroll) { - scrollToShowItem(mSelectedItems.back()); + scrollToShowItem(mSelectedItems.back(), constraint_rect); // continue scrolling until animated layout change is done - if (getCompletedFilterGeneration() >= mFilter->getMinRequiredGeneration() && - (!needsArrange() || !is_visible)) + if (!filter_modified_and_active + && (!needsArrange() || !is_visible)) { mNeedsScroll = FALSE; } @@ -2077,15 +2129,13 @@ void LLFolderView::updateRenamerPosition() screenPointToLocal( x, y, &x, &y ); mRenamer->setOrigin( x, y ); - S32 scroller_height = 0; - S32 scroller_width = gViewerWindow->getWindowWidth(); - BOOL dummy_bool; + LLRect scroller_rect(0, 0, gViewerWindow->getWindowWidth(), 0); if (mScrollContainer) { - mScrollContainer->calcVisibleSize( &scroller_width, &scroller_height, &dummy_bool, &dummy_bool); + scroller_rect = mScrollContainer->getContentWindowRect(); } - S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_width - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); + S32 width = llmax(llmin(mRenameItem->getRect().getWidth() - x, scroller_rect.getWidth() - x - getRect().mLeft), MINIMUM_RENAMER_WIDTH); S32 height = llfloor(sFont->getLineHeight() + RENAME_HEIGHT_PAD); mRenamer->reshape( width, height, TRUE ); } diff --git a/indra/newview/llfolderview.h b/indra/newview/llfolderview.h index 8d9d52cd17..a05dec3193 100644 --- a/indra/newview/llfolderview.h +++ b/indra/newview/llfolderview.h @@ -240,7 +240,7 @@ public: virtual void deleteAllChildren(); void scrollToShowSelection(); - void scrollToShowItem(LLFolderViewItem* item); + void scrollToShowItem(LLFolderViewItem* item, const LLRect& constraint_rect); void setScrollContainer( LLScrollContainer* parent ) { mScrollContainer = parent; } LLRect getVisibleRect(); @@ -299,8 +299,8 @@ protected: LLLineEditor* mRenamer; BOOL mNeedsScroll; - LLFolderViewItem* mLastScrollItem; - LLCoordGL mLastScrollOffset; + BOOL mPinningSelectedItem; + LLRect mScrollConstraintRect; BOOL mNeedsAutoSelect; BOOL mAutoSelectOverride; BOOL mNeedsAutoRename; diff --git a/indra/newview/llfolderviewitem.cpp b/indra/newview/llfolderviewitem.cpp index 69ce2f0e0e..8961508bcc 100644 --- a/indra/newview/llfolderviewitem.cpp +++ b/indra/newview/llfolderviewitem.cpp @@ -233,7 +233,7 @@ void LLFolderViewItem::refreshFromListener() // temporary attempt to display the inventory folder in the user locale. if (LLAssetType::lookupIsProtectedCategoryType(preferred_type)) { - mLabel = LLTrans::getString("InvFolder " + mLabel); + LLTrans::findString(mLabel, "InvFolder " + mLabel); }; setIcon(mListener->getIcon()); @@ -2115,6 +2115,14 @@ BOOL LLFolderViewFolder::handleMouseDown( S32 x, S32 y, MASK mask ) BOOL LLFolderViewFolder::handleDoubleClick( S32 x, S32 y, MASK mask ) { + const LLUUID &cat_uuid = getListener()->getUUID(); + const LLViewerInventoryCategory *cat = gInventory.getCategory(cat_uuid); + if (cat && cat->getPreferredType() == LLAssetType::AT_OUTFIT) + { + getListener()->performAction(NULL, NULL,"replaceoutfit"); + return TRUE; + } + BOOL handled = FALSE; if( mIsOpen ) { diff --git a/indra/newview/llgroupactions.cpp b/indra/newview/llgroupactions.cpp index b14f23f9cf..10a17476d9 100644 --- a/indra/newview/llgroupactions.cpp +++ b/indra/newview/llgroupactions.cpp @@ -37,10 +37,8 @@ #include "llagent.h" #include "llfloaterreg.h" -#include "llimview.h" // for gIMMgr #include "llgroupmgr.h" -#include "llavataractions.h" -#include "llviewercontrol.h" +#include "llimview.h" // for gIMMgr #include "llsidetray.h" #include "llcommandhandler.h" @@ -55,7 +53,7 @@ public: // requires trusted browser to trigger LLGroupHandler() : LLCommandHandler("group", true) { } bool handle(const LLSD& tokens, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { if (tokens.size() < 1) { @@ -103,126 +101,6 @@ public: }; LLGroupHandler gGroupHandler; - - -// LLGroupActions::teleport helper -// -// Method is offerTeleport should be called. -// First it checks, whether LLGroupMgr contains LLGroupMgrGroupData for this group already. -// If it's there, processMembersList can be called, which builds vector of ID's for online members and -// calls LLAvatarActions::offerTeleport. -// If LLGroupMgr doesn't contain LLGroupMgrGroupData, then ID of group should be saved in -// mID or queue, if mID is not empty. After that processQueue uses ID from mID or queue, -// registers LLGroupTeleporter as observer at LLGroupMgr and sends request for group members. -// LLGroupMgr notifies about response on this request by calling method 'changed'. -// It calls processMembersList, sets mID to null, to indicate that current group is processed, -// and calls processQueue to process remaining groups. -// The reason of calling of LLGroupMgr::addObserver and LLGroupMgr::removeObserver in -// processQueue and 'changed' methods is that LLGroupMgr notifies observers of only particular group, -// so, for each group mID should be updated and addObserver/removeObserver is called. - -class LLGroupTeleporter : public LLGroupMgrObserver -{ -public: - LLGroupTeleporter() : LLGroupMgrObserver(LLUUID()) {} - - void offerTeleport(const LLUUID& group_id); - - // LLGroupMgrObserver trigger - virtual void changed(LLGroupChange gc); -private: - void processQueue(); - void processMembersList(LLGroupMgrGroupData* gdatap); - - std::queue<LLUUID> mGroupsQueue; -}; - -void LLGroupTeleporter::offerTeleport(const LLUUID& group_id) -{ - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(group_id); - - if (!gdatap || !gdatap->isMemberDataComplete()) - { - if (mID.isNull()) - mID = group_id; - else - // Not null mID means that user requested next group teleport before - // previous group is processed, so this group goes to queue - mGroupsQueue.push(group_id); - - processQueue(); - } - else - { - processMembersList(gdatap); - } -} - -// Sends request for group in mID or one group in queue -void LLGroupTeleporter::processQueue() -{ - // Get group from queue, if mID is empty - if (mID.isNull() && !mGroupsQueue.empty()) - { - mID = mGroupsQueue.front(); - mGroupsQueue.pop(); - } - - if (mID.notNull()) - { - LLGroupMgr::getInstance()->addObserver(this); - LLGroupMgr::getInstance()->sendGroupMembersRequest(mID); - } -} - -// Collects all online members of group and offers teleport to them -void LLGroupTeleporter::processMembersList(LLGroupMgrGroupData* gdatap) -{ - U32 limit = gSavedSettings.getU32("GroupTeleportMembersLimit"); - - LLDynamicArray<LLUUID> ids; - for (LLGroupMgrGroupData::member_list_t::iterator iter = gdatap->mMembers.begin(); iter != gdatap->mMembers.end(); iter++) - { - LLGroupMemberData* member = iter->second; - if (!member) - continue; - - if (member->getID() == gAgent.getID()) - // No need to teleport own avatar - continue; - - if (member->getOnlineStatus() == "Online") - ids.push_back(member->getID()); - - if ((U32)ids.size() >= limit) - break; - } - - LLAvatarActions::offerTeleport(ids); -} - -// LLGroupMgrObserver trigger -void LLGroupTeleporter::changed(LLGroupChange gc) -{ - if (gc == GC_MEMBER_DATA) - { - LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); - - if (gdatap && gdatap->isMemberDataComplete()) - processMembersList(gdatap); - - LLGroupMgr::getInstance()->removeObserver(this); - - // group in mID is processed - mID.setNull(); - - // process other groups in queue, if any - processQueue(); - } -} - -static LLGroupTeleporter sGroupTeleporter; - // static void LLGroupActions::search() { @@ -285,6 +163,19 @@ void LLGroupActions::show(const LLUUID& group_id) LLSideTray::getInstance()->showPanel("panel_group_info_sidetray", params); } +void LLGroupActions::refresh_notices() +{ + if(!isGroupUIVisible()) + return; + + LLSD params; + params["group_id"] = LLUUID::null; + params["open_tab_name"] = "panel_group_info_sidetray"; + params["action"] = "refresh_notices"; + + LLSideTray::getInstance()->showPanel("panel_group_info_sidetray", params); +} + //static void LLGroupActions::refresh(const LLUUID& group_id) { @@ -349,9 +240,25 @@ void LLGroupActions::startChat(const LLUUID& group_id) } // static -void LLGroupActions::offerTeleport(const LLUUID& group_id) +bool LLGroupActions::isAvatarMemberOfGroup(const LLUUID& group_id, const LLUUID& avatar_id) { - sGroupTeleporter.offerTeleport(group_id); + if(group_id.isNull() || avatar_id.isNull()) + { + return false; + } + + LLGroupMgrGroupData* group_data = LLGroupMgr::getInstance()->getGroupData(group_id); + if(!group_data) + { + return false; + } + + if(group_data->mMembers.end() == group_data->mMembers.find(avatar_id)) + { + return false; + } + + return true; } //-- Private methods ---------------------------------------------------------- diff --git a/indra/newview/llgroupactions.h b/indra/newview/llgroupactions.h index 70170d3cfe..9fe1da8af2 100644 --- a/indra/newview/llgroupactions.h +++ b/indra/newview/llgroupactions.h @@ -68,6 +68,11 @@ public: static void refresh(const LLUUID& group_id); /** + * Refresh group notices panel. + */ + static void refresh_notices(); + + /** * Refresh group information panel. */ static void createGroup(); @@ -83,9 +88,13 @@ public: static void startChat(const LLUUID& group_id); /** - * Offers teleport for online members of group + * Returns true if avatar is in group. + * + * Note that data about group members is loaded from server. + * If data has not been loaded yet, function will return inaccurate result. + * See LLGroupMgr::sendGroupMembersRequest */ - static void offerTeleport(const LLUUID& group_id); + static bool isAvatarMemberOfGroup(const LLUUID& group_id, const LLUUID& avatar_id); private: static bool onLeaveGroup(const LLSD& notification, const LLSD& response); diff --git a/indra/newview/llgroupmgr.cpp b/indra/newview/llgroupmgr.cpp index 0ba1019765..5e50fad008 100644 --- a/indra/newview/llgroupmgr.cpp +++ b/indra/newview/llgroupmgr.cpp @@ -809,7 +809,7 @@ static void formatDateString(std::string &date_string) std::string day = result[2]; // ISO 8601 date format - date_string = llformat("%04s-%02s-%02s", year.c_str(), month.c_str(), day.c_str()); + date_string = llformat("%02s/%02s/%04s", month.c_str(), day.c_str(), year.c_str()); } } diff --git a/indra/newview/llhudeffectlookat.cpp b/indra/newview/llhudeffectlookat.cpp index 3a5bec2be0..e366340a10 100644 --- a/indra/newview/llhudeffectlookat.cpp +++ b/indra/newview/llhudeffectlookat.cpp @@ -610,7 +610,9 @@ bool LLHUDEffectLookAt::calcTargetPosition() } LLVOAvatar* source_avatar = (LLVOAvatar*)(LLViewerObject*)mSourceObject; - + if (!source_avatar->isBuilt()) + return false; + if (target_obj && target_obj->mDrawable.notNull()) { LLQuaternion target_rot; diff --git a/indra/newview/llhudmanager.cpp b/indra/newview/llhudmanager.cpp index 8588de0fa0..bdb8dadfb4 100644 --- a/indra/newview/llhudmanager.cpp +++ b/indra/newview/llhudmanager.cpp @@ -62,10 +62,11 @@ LLHUDManager::~LLHUDManager() { } +static LLFastTimer::DeclareTimer FTM_HUD_EFFECTS("Hud Effects"); void LLHUDManager::updateEffects() { - LLFastTimer ftm(LLFastTimer::FTM_HUD_EFFECTS); + LLFastTimer ftm(FTM_HUD_EFFECTS); S32 i; for (i = 0; i < mHUDEffects.count(); i++) { diff --git a/indra/newview/llhudobject.cpp b/indra/newview/llhudobject.cpp index bdff492948..dc55aba0db 100644 --- a/indra/newview/llhudobject.cpp +++ b/indra/newview/llhudobject.cpp @@ -254,10 +254,12 @@ LLHUDEffect *LLHUDObject::addHUDEffect(const U8 type) return hud_objectp; } +static LLFastTimer::DeclareTimer FTM_HUD_UPDATE("Update Hud"); + // static void LLHUDObject::updateAll() { - LLFastTimer ftm(LLFastTimer::FTM_HUD_UPDATE); + LLFastTimer ftm(FTM_HUD_UPDATE); LLHUDText::updateAll(); LLHUDIcon::updateAll(); sortObjects(); diff --git a/indra/newview/llhudtext.cpp b/indra/newview/llhudtext.cpp index c71262c311..8086400493 100644 --- a/indra/newview/llhudtext.cpp +++ b/indra/newview/llhudtext.cpp @@ -957,7 +957,7 @@ void LLHUDText::updateAll() { continue; } - if (src_textp->mSoftScreenRect.rectInRect(&dst_textp->mSoftScreenRect)) + if (src_textp->mSoftScreenRect.overlaps(dst_textp->mSoftScreenRect)) { LLRectf intersect_rect = src_textp->mSoftScreenRect; intersect_rect.intersectWith(dst_textp->mSoftScreenRect); diff --git a/indra/newview/llhudview.cpp b/indra/newview/llhudview.cpp index 6f22a68327..027cd2ab07 100644 --- a/indra/newview/llhudview.cpp +++ b/indra/newview/llhudview.cpp @@ -39,7 +39,6 @@ #include "llcoord.h" // viewer includes -#include "llagent.h" #include "llcallingcard.h" #include "llviewercontrol.h" #include "llfloaterworldmap.h" @@ -47,14 +46,18 @@ #include "lltracker.h" #include "llviewercamera.h" #include "llui.h" +#include "lluictrlfactory.h" LLHUDView *gHUDView = NULL; const S32 HUD_ARROW_SIZE = 32; -LLHUDView::LLHUDView() -: LLPanel() + + +LLHUDView::LLHUDView(const LLRect& r) { + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_hud.xml"); + setShape(r, true); } LLHUDView::~LLHUDView() @@ -65,6 +68,7 @@ LLHUDView::~LLHUDView() void LLHUDView::draw() { LLTracker::drawHUDArrow(); + LLView::draw(); } @@ -90,4 +94,3 @@ BOOL LLHUDView::handleMouseDown(S32 x, S32 y, MASK mask) } return LLView::handleMouseDown(x, y, mask); } - diff --git a/indra/newview/llhudview.h b/indra/newview/llhudview.h index 7859e7f8be..05ff9c8596 100644 --- a/indra/newview/llhudview.h +++ b/indra/newview/llhudview.h @@ -42,7 +42,7 @@ class LLHUDView : public LLPanel { public: - LLHUDView(); + LLHUDView(const LLRect& rect); virtual ~LLHUDView(); virtual void draw(); diff --git a/indra/newview/llimhandler.cpp b/indra/newview/llimhandler.cpp new file mode 100644 index 0000000000..0262e40803 --- /dev/null +++ b/indra/newview/llimhandler.cpp @@ -0,0 +1,127 @@ +/** + * @file llimhandler.cpp + * @brief Notification Handler Class for IM notifications + * + * $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 "llviewerprecompiledheaders.h" // must be first include + +#include "llnotificationhandler.h" + +#include "llbottomtray.h" +#include "llviewercontrol.h" +#include "lltoastimpanel.h" + +using namespace LLNotificationsUI; + +//-------------------------------------------------------------------------- +LLIMHandler::LLIMHandler() +{ + + // getting a Chiclet and creating params for a channel + LLBottomTray* tray = LLBottomTray::getInstance(); + mChiclet = tray->getSysWell(); + + LLChannelManager::Params p; + // *TODO: createNotificationChannel method + p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); + p.channel_right_bound = tray->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); + p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); + + // Getting a Channel for our notifications + mChannel = LLChannelManager::getInstance()->createChannel(p); + +} + +//-------------------------------------------------------------------------- +LLIMHandler::~LLIMHandler() +{ +} + +//-------------------------------------------------------------------------- +void LLIMHandler::processNotification(const LLSD& notify) +{ + LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID()); + + if(!notification) + return; + + if(notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "change") + { + LLSD substitutions = notification->getSubstitutions(); + + LLToastIMPanel::Params im_p; + im_p.notification = notification; + im_p.avatar_id = substitutions["FROM_ID"].asUUID(); + im_p.from = substitutions["FROM"].asString(); + im_p.time = substitutions["TIME"].asString(); + im_p.message = substitutions["MESSAGE"].asString(); + im_p.session_id = substitutions["SESSION_ID"].asUUID(); + + LLToastIMPanel* im_box = new LLToastIMPanel(im_p); + + LLToast::Params p; + p.id = notification->getID(); + p.notification = notification; + p.panel = im_box; + p.can_be_stored = false; + p.on_toast_destroy = boost::bind(&LLIMHandler::onToastDestroy, this, _1); + mChannel->addToast(p); + + + static_cast<LLNotificationChiclet*>(mChiclet)->updateUreadIMNotifications(); + } + else if (notify["sigtype"].asString() == "delete") + { + mChannel->killToastByNotificationID(notification->getID()); + } +} + +//-------------------------------------------------------------------------- +void LLIMHandler::onToastDestroy(LLToast* toast) +{ + toast->closeFloater(); + static_cast<LLNotificationChiclet*>(mChiclet)->updateUreadIMNotifications(); +} + +//-------------------------------------------------------------------------- +void LLIMHandler::onChicletClick(void) +{ +} + +//-------------------------------------------------------------------------- +void LLIMHandler::onChicletClose(void) +{ +} + +//-------------------------------------------------------------------------- + + + diff --git a/indra/newview/llimpanel.cpp b/indra/newview/llimpanel.cpp index eeb127c878..9cf3e57e22 100644 --- a/indra/newview/llimpanel.cpp +++ b/indra/newview/llimpanel.cpp @@ -77,7 +77,6 @@ #include "lluictrlfactory.h" #include "llviewerwindow.h" #include "lllogchat.h" -#include "llfloaterhtml.h" #include "llweb.h" #include "llhttpclient.h" #include "llmutelist.h" @@ -1097,66 +1096,58 @@ BOOL LLFloaterIMPanel::postBuild() mVisibleSignal.connect(boost::bind(&LLFloaterIMPanel::onVisibilityChange, this, _2)); - requires<LLLineEditor>("chat_editor"); - requires<LLTextEditor>("im_history"); - - if (checkRequirements()) - { - mInputEditor = getChild<LLLineEditor>("chat_editor"); - mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); - mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); - mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); - mInputEditor->setCommitCallback( onCommitChat, this ); - mInputEditor->setCommitOnFocusLost( FALSE ); - mInputEditor->setRevertOnEsc( FALSE ); - mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); + mInputEditor = getChild<LLLineEditor>("chat_editor"); + mInputEditor->setFocusReceivedCallback( onInputEditorFocusReceived, this ); + mInputEditor->setFocusLostCallback( onInputEditorFocusLost, this ); + mInputEditor->setKeystrokeCallback( onInputEditorKeystroke, this ); + mInputEditor->setCommitCallback( onCommitChat, this ); + mInputEditor->setCommitOnFocusLost( FALSE ); + mInputEditor->setRevertOnEsc( FALSE ); + mInputEditor->setReplaceNewlinesWithSpaces( FALSE ); - childSetAction("profile_callee_btn", onClickProfile, this); - childSetAction("group_info_btn", onClickGroupInfo, this); + childSetAction("profile_callee_btn", onClickProfile, this); + childSetAction("group_info_btn", onClickGroupInfo, this); - childSetAction("start_call_btn", onClickStartCall, this); - childSetAction("end_call_btn", onClickEndCall, this); - childSetAction("send_btn", onClickSend, this); - childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this); + childSetAction("start_call_btn", onClickStartCall, this); + childSetAction("end_call_btn", onClickEndCall, this); + childSetAction("send_btn", onClickSend, this); + childSetAction("toggle_active_speakers_btn", onClickToggleActiveSpeakers, this); - childSetAction("moderator_kick_speaker", onKickSpeaker, this); - //LLButton* close_btn = getChild<LLButton>("close_btn"); - //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); + childSetAction("moderator_kick_speaker", onKickSpeaker, this); + //LLButton* close_btn = getChild<LLButton>("close_btn"); + //close_btn->setClickedCallback(&LLFloaterIMPanel::onClickClose, this); - mHistoryEditor = getChild<LLViewerTextEditor>("im_history"); - mHistoryEditor->setParseHTML(TRUE); - mHistoryEditor->setParseHighlights(TRUE); + mHistoryEditor = getChild<LLViewerTextEditor>("im_history"); + mHistoryEditor->setParseHTML(TRUE); + mHistoryEditor->setParseHighlights(TRUE); - if ( IM_SESSION_GROUP_START == mDialog ) - { - childSetEnabled("profile_btn", FALSE); - } - - if(!mProfileButtonEnabled) - { - childSetEnabled("profile_callee_btn", FALSE); - } + if ( IM_SESSION_GROUP_START == mDialog ) + { + childSetEnabled("profile_btn", FALSE); + } + + if(!mProfileButtonEnabled) + { + childSetEnabled("profile_callee_btn", FALSE); + } - sTitleString = getString("title_string"); - sTypingStartString = getString("typing_start_string"); - sSessionStartString = getString("session_start_string"); + sTitleString = getString("title_string"); + sTypingStartString = getString("typing_start_string"); + sSessionStartString = getString("session_start_string"); - if (mSpeakerPanel) - { - mSpeakerPanel->refreshSpeakers(); - } - - if (mDialog == IM_NOTHING_SPECIAL) - { - childSetAction("mute_btn", onClickMuteVoice, this); - childSetCommitCallback("speaker_volume", onVolumeChange, this); - } + if (mSpeakerPanel) + { + mSpeakerPanel->refreshSpeakers(); + } - setDefaultBtn("send_btn"); - return TRUE; + if (mDialog == IM_NOTHING_SPECIAL) + { + childSetAction("mute_btn", onClickMuteVoice, this); + childSetCommitCallback("speaker_volume", onVolumeChange, this); } - return FALSE; + setDefaultBtn("send_btn"); + return TRUE; } void* LLFloaterIMPanel::createSpeakersPanel(void* data) @@ -1385,8 +1376,7 @@ void LLFloaterIMPanel::addHistoryLine(const std::string &utf8msg, const LLColor4 else { // Convert the name to a hotlink and add to message. - const LLStyleSP &source_style = LLStyleMap::instance().lookupAgent(source); - mHistoryEditor->appendStyledText(name + separator_string, false, prepend_newline, source_style); + mHistoryEditor->appendStyledText(name + separator_string, false, prepend_newline, LLStyleMap::instance().lookupAgent(source)); } prepend_newline = false; } @@ -2011,13 +2001,29 @@ bool LLFloaterIMPanel::onConfirmForceCloseError(const LLSD& notification, const LLIMFloater::LLIMFloater(const LLUUID& session_id) : LLFloater(session_id), + mControlPanel(NULL), mSessionID(session_id), mLastMessageIndex(-1), + mLastFromName(), mDialog(IM_NOTHING_SPECIAL), mHistoryEditor(NULL), mInputEditor(NULL), mPositioned(false) { + LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); + if(session) + { + mDialog = session->mType; + } + + if (mDialog == IM_NOTHING_SPECIAL) + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelIMControl, this); + } + else + { + mFactoryMap["panel_im_control_panel"] = LLCallbackMap(createPanelGroupControl, this); + } // LLUICtrlFactory::getInstance()->buildFloater(this, "floater_im_session.xml"); } @@ -2089,22 +2095,19 @@ LLIMFloater::~LLIMFloater() //virtual BOOL LLIMFloater::postBuild() { - LLPanelIMControlPanel* im_control_panel = getChild<LLPanelIMControlPanel>("panel_im_control_panel"); - LLIMModel::LLIMSession* session = get_if_there(LLIMModel::instance().sSessionsMap, mSessionID, (LLIMModel::LLIMSession*)NULL); if(session) { mOtherParticipantUUID = session->mOtherParticipantID; - im_control_panel->setAvatarId(session->mOtherParticipantID); - mDialog = session->mType; + mControlPanel->setID(session->mOtherParticipantID); } LLButton* slide_left = getChild<LLButton>("slide_left_btn"); - slide_left->setVisible(im_control_panel->getVisible()); + slide_left->setVisible(mControlPanel->getVisible()); slide_left->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); LLButton* slide_right = getChild<LLButton>("slide_right_btn"); - slide_right->setVisible(!im_control_panel->getVisible()); + slide_right->setVisible(!mControlPanel->getVisible()); slide_right->setClickedCallback(boost::bind(&LLIMFloater::onSlide, this)); mInputEditor = getChild<LLLineEditor>("chat_editor"); @@ -2131,6 +2134,28 @@ BOOL LLIMFloater::postBuild() return TRUE; } + + +// static +void* LLIMFloater::createPanelIMControl(void* userdata) +{ + LLIMFloater *self = (LLIMFloater*)userdata; + self->mControlPanel = new LLPanelIMControlPanel(); + self->mControlPanel->setXMLFilename("panel_im_control_panel.xml"); + return self->mControlPanel; +} + + +// static +void* LLIMFloater::createPanelGroupControl(void* userdata) +{ + LLIMFloater *self = (LLIMFloater*)userdata; + self->mControlPanel = new LLPanelGroupControlPanel(); + self->mControlPanel->setXMLFilename("panel_group_control_panel.xml"); + return self->mControlPanel; +} + + const U32 UNDOCK_LEAP_HEIGHT = 12; const U32 DOCK_ICON_HEIGHT = 6; @@ -2141,12 +2166,16 @@ void LLIMFloater::onFocusLost() // (hence we are no longer focused) if (isDocked()) { - // app not quitting - closeFloater(false); + LLIMFloater* floater = LLFloaterReg::getTypedInstance<LLIMFloater>("impanel", mSessionID); + if (floater) + { + floater->setVisible(false); + } } } + //virtual void LLIMFloater::setDocked(bool docked, bool pop_on_undock) { @@ -2177,7 +2206,7 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) iter != inst_list.end(); ++iter) { LLIMFloater* floater = dynamic_cast<LLIMFloater*>(*iter); - if (floater) + if (floater && floater->isDocked()) { floater->setVisible(false); } @@ -2190,30 +2219,66 @@ LLIMFloater* LLIMFloater::show(const LLUUID& session_id) return floater; } -void LLIMFloater::updateMessages() +//static +bool LLIMFloater::toggle(const LLUUID& session_id) { + LLIMFloater* floater = LLFloaterReg::findTypedInstance<LLIMFloater>("impanel", session_id); + if (floater && floater->getVisible()) + { + // clicking on chiclet to close floater just hides it to maintain existing + // scroll/text entry state + floater->setVisible(false); + return false; + } + else + { + // ensure the list of messages is updated when floater is made visible + show(session_id); + // update number of unread notifications in the SysWell + LLBottomTray::getInstance()->getSysWell()->updateUreadIMNotifications(); + return true; + } +} +void LLIMFloater::updateMessages() +{ std::list<LLSD> messages = LLIMModel::instance().getMessages(mSessionID, mLastMessageIndex+1); if (messages.size()) { + LLUIColor divider_color = LLUIColorTable::instance().getColor("LtGray_50"); + LLUIColor chat_color = LLUIColorTable::instance().getColor("IMChatColor"); + std::ostringstream message; std::list<LLSD>::const_reverse_iterator iter = messages.rbegin(); std::list<LLSD>::const_reverse_iterator iter_end = messages.rend(); for (; iter != iter_end; ++iter) { LLSD msg = *iter; - - message << msg["from"].asString() << " : " << msg["time"].asString() << "\n " << msg["message"].asString() << "\n"; - + + const bool prepend_newline = true; + std::string from = msg["from"].asString(); + if (mLastFromName != from) + { + message << from << " ----- " << msg["time"].asString(); + mHistoryEditor->appendColoredText(message.str(), false, + prepend_newline, divider_color); + message.str(""); + mLastFromName = from; + } + + message << msg["message"].asString(); + mHistoryEditor->appendColoredText(message.str(), false, + prepend_newline, chat_color); + message.str(""); + mLastMessageIndex = msg["index"].asInteger(); } - mHistoryEditor->appendText(message.str(), false, false); mHistoryEditor->setCursorAndScrollToEnd(); } - } + // static void LLIMFloater::onInputEditorFocusReceived( LLFocusableElement* caller, void* userdata ) { diff --git a/indra/newview/llimpanel.h b/indra/newview/llimpanel.h index dcb0f2416f..284a486b0f 100644 --- a/indra/newview/llimpanel.h +++ b/indra/newview/llimpanel.h @@ -47,6 +47,7 @@ class LLInventoryItem; class LLInventoryCategory; class LLIMSpeakerMgr; class LLPanelActiveSpeakers; +class LLPanelChatControlPanel; class LLVoiceChannel : public LLVoiceClientStatusObserver { @@ -383,8 +384,13 @@ public: // LLFloater overrides /*virtual*/ void setDocked(bool docked, bool pop_on_undock = true); + // Make IM conversion visible and update the message history static LLIMFloater* show(const LLUUID& session_id); + // Toggle panel specified by session_id + // Returns true iff panel became visible + static bool toggle(const LLUUID& session_id); + // get new messages from LLIMModel void updateMessages(); static void onSendMsg( LLUICtrl*, void*); @@ -405,11 +411,16 @@ private: static void onInputEditorFocusLost(LLFocusableElement* caller, void* userdata); static void onInputEditorKeystroke(LLLineEditor* caller, void* userdata); void setTyping(BOOL typing); - - void onSlide(); + void onSlide(); + static void* createPanelIMControl(void* userdata); + static void* createPanelGroupControl(void* userdata); + LLPanelChatControlPanel* mControlPanel; LLUUID mSessionID; S32 mLastMessageIndex; + // username of last user who added text to this conversation, used to + // suppress duplicate username divider bars + std::string mLastFromName; EInstantMessage mDialog; LLUUID mOtherParticipantUUID; LLViewerTextEditor* mHistoryEditor; diff --git a/indra/newview/llimview.cpp b/indra/newview/llimview.cpp index b45d36e7ac..0d773a7c54 100644 --- a/indra/newview/llimview.cpp +++ b/indra/newview/llimview.cpp @@ -69,10 +69,10 @@ #include "llviewerwindow.h" #include "llnotify.h" #include "llviewerregion.h" -#include "llviewertexteditor.h" #include "lltrans.h" #include "llfirstuse.h" +#include "llagentui.h" // // Globals @@ -105,6 +105,8 @@ void toast_callback(const LLSD& msg){ args["MESSAGE"] = msg["message"]; args["TIME"] = msg["time"]; args["FROM"] = msg["from"]; + args["FROM_ID"] = msg["from_id"]; + args["SESSION_ID"] = msg["session_id"]; LLNotifications::instance().add("IMToast", args, LLSD(), boost::bind(&LLFloaterChatterBox::onOpen, LLFloaterChatterBox::getInstance(), msg["session_id"].asUUID())); } @@ -125,7 +127,7 @@ void LLIMModel::testMessages() bot1_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot1_id); newSession(bot1_session_id, from, IM_NOTHING_SPECIAL, bot1_id); - addMessage(bot1_session_id, from, "Test Message: Hi from testerbot land!"); + addMessage(bot1_session_id, from, bot1_id, "Test Message: Hi from testerbot land!"); LLUUID bot2_id; std::string firstname[] = {"Roflcopter", "Joe"}; @@ -138,8 +140,8 @@ void LLIMModel::testMessages() bot2_id.generate(from); LLUUID bot2_session_id = LLIMMgr::computeSessionID(IM_NOTHING_SPECIAL, bot2_id); newSession(bot2_session_id, from, IM_NOTHING_SPECIAL, bot2_id); - addMessage(bot2_session_id, from, "Test Message: Can I haz bear? "); - addMessage(bot2_session_id, from, "Test Message: OMGWTFBBQ."); + addMessage(bot2_session_id, from, bot2_id, "Test Message: Can I haz bear? "); + addMessage(bot2_session_id, from, bot2_id, "Test Message: OMGWTFBBQ."); } @@ -219,7 +221,7 @@ bool LLIMModel::addToHistory(LLUUID session_id, std::string from, std::string ut } -bool LLIMModel::addMessage(LLUUID session_id, std::string from, std::string utf8_text) { +bool LLIMModel::addMessage(LLUUID session_id, std::string from, LLUUID from_id, std::string utf8_text) { LLIMSession* session = get_if_there(sSessionsMap, session_id, (LLIMSession*)NULL); @@ -232,7 +234,7 @@ bool LLIMModel::addMessage(LLUUID session_id, std::string from, std::string utf8 addToHistory(session_id, from, utf8_text); std::string agent_name; - gAgent.buildFullname(agent_name); + LLAgentUI::buildFullname(agent_name); session->mNumUnread++; @@ -241,6 +243,9 @@ bool LLIMModel::addMessage(LLUUID session_id, std::string from, std::string utf8 arg["session_id"] = session_id; arg["num_unread"] = session->mNumUnread; arg["message"] = utf8_text; + arg["from"] = from; + arg["from_id"] = from_id; + arg["time"] = LLLogChat::timestamp(false); mChangedSignal(arg); return true; @@ -265,7 +270,7 @@ const std::string& LLIMModel::getName(LLUUID session_id) void LLIMModel::sendTypingState(LLUUID session_id, LLUUID other_participant_id, BOOL typing) { std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); pack_instant_message( gMessageSystem, @@ -286,7 +291,7 @@ void LLIMModel::sendLeaveSession(LLUUID session_id, LLUUID other_participant_id) if(session_id.notNull()) { std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); pack_instant_message( gMessageSystem, gAgent.getID(), @@ -311,7 +316,7 @@ void LLIMModel::sendMessage(const std::string& utf8_text, { std::string name; bool sent = false; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); const LLRelationship* info = NULL; info = LLAvatarTracker::instance().getBuddyInfo(other_participant_id); @@ -378,13 +383,13 @@ void LLIMModel::sendMessage(const std::string& utf8_text, { // Do we have to replace the /me's here? std::string from; - gAgent.buildFullname(from); + LLAgentUI::buildFullname(from); LLIMModel::instance().addToHistory(im_session_id, from, utf8_text); //local echo for the legacy communicate panel std::string history_echo; std::string utf8_copy = utf8_text; - gAgent.buildFullname(history_echo); + LLAgentUI::buildFullname(history_echo); // Look for IRC-style emotes here. @@ -434,7 +439,7 @@ void session_starter_helper( msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); msg->addStringFast(_PREHASH_FromAgentName, name); msg->addStringFast(_PREHASH_Message, LLStringUtil::null); @@ -847,8 +852,13 @@ BOOL LLIncomingCallDialog::postBuild() EInstantMessage type = (EInstantMessage)mPayload["type"].asInteger(); std::string call_type = getString("VoiceInviteP2P"); - std::string caller_name = mPayload["caller_name"].asString() + " "; - setTitle(caller_name + call_type); + std::string caller_name = mPayload["caller_name"].asString(); + if (caller_name == "anonymous") + { + caller_name = getString("anonymous"); + } + + setTitle(caller_name + " " + call_type); // If it is not a P2P invite, then it's an AdHoc invite if ( type != IM_SESSION_P2P_INVITE ) @@ -856,8 +866,8 @@ BOOL LLIncomingCallDialog::postBuild() call_type = getString("VoiceInviteAdHoc"); } - LLViewerTextEditor* text = getChild<LLViewerTextEditor>("caller name"); - text->setEmbeddedText(caller_name + call_type); + LLUICtrl* caller_name_widget = getChild<LLUICtrl>("caller name"); + caller_name_widget->setValue(caller_name + " " + call_type); LLAvatarIconCtrl* icon = getChild<LLAvatarIconCtrl>("avatar_icon"); icon->setValue(caller_id); @@ -1227,7 +1237,7 @@ void LLIMMgr::addMessage( //<< "*** position: " << position << std::endl; floater->addHistoryLine(bonus_info.str(), LLUIColorTable::instance().getColor("SystemChatColor")); - LLIMModel::instance().addMessage(new_session_id, from, bonus_info.str()); + LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, bonus_info.str()); } make_ui_sound("UISndNewIncomingIMSession"); @@ -1247,7 +1257,7 @@ void LLIMMgr::addMessage( floater->addHistoryLine(msg, color, true, other_participant_id, from); // Insert linked name to front of message } - LLIMModel::instance().addMessage(new_session_id, from, msg); + LLIMModel::instance().addMessage(new_session_id, from, other_participant_id, msg); if( !LLFloaterReg::instanceVisible("communicate") && !floater->getVisible()) { @@ -1307,6 +1317,19 @@ void LLIMMgr::notifyNewIM() } } +S32 LLIMMgr::getNumberOfUnreadIM() +{ + std::map<LLUUID, LLIMModel::LLIMSession*>::iterator it; + + S32 num = 0; + for(it = LLIMModel::sSessionsMap.begin(); it != LLIMModel::sSessionsMap.end(); ++it) + { + num += (*it).second->mNumUnread; + } + + return num; +} + void LLIMMgr::clearNewIMNotification() { mIMReceived = FALSE; @@ -1352,44 +1375,9 @@ LLUUID LLIMMgr::addSession( EInstantMessage dialog, const LLUUID& other_participant_id) { - LLUUID session_id = computeSessionID(dialog, other_participant_id); - - LLFloaterIMPanel* floater = findFloaterBySession(session_id); - if(!floater) - { - LLDynamicArray<LLUUID> ids; - ids.put(other_participant_id); - - floater = createFloater( - session_id, - other_participant_id, - name, - ids, - dialog, - TRUE); - - noteOfflineUsers(floater, ids); - //LLFloaterReg::showInstance("communicate", session_id); - // *NOTE: Is this right? Or should we only do it for - // dialog == IM_NOTHING_SPECIAL and some group types? - LLIMFloater::show(session_id); - - // Only warn for regular IMs - not group IMs - if( dialog == IM_NOTHING_SPECIAL ) - { - noteMutedUsers(floater, ids); - } - } - else - { - // *TODO: Remove this? Otherwise old communicate window opens on - // second initiation of IM session from People panel? - // floater->openFloater(); - } - //mTabContainer->selectTabPanel(panel); - floater->setInputFocus(TRUE); - notifyObserverSessionAdded(floater->getSessionID(), name, other_participant_id); - return floater->getSessionID(); + LLDynamicArray<LLUUID> ids; + ids.put(other_participant_id); + return addSession(name, dialog, other_participant_id, ids); } // Adds a session using the given session_id. If the session already exists @@ -1405,9 +1393,7 @@ LLUUID LLIMMgr::addSession( return LLUUID::null; } - LLUUID session_id = computeSessionID( - dialog, - other_participant_id); + LLUUID session_id = computeSessionID(dialog,other_participant_id); LLFloaterIMPanel* floater = findFloaterBySession(session_id); if(!floater) @@ -1418,15 +1404,13 @@ LLUUID LLIMMgr::addSession( session_id, other_participant_id, name, - ids, dialog, - TRUE); + TRUE, + ids); if ( !floater ) return LLUUID::null; noteOfflineUsers(floater, ids); - // *BUG: Is this correct? What do we want to spawn for group IMs? - // LLFloaterReg::showInstance("communicate", session_id); // Only warn for regular IMs - not group IMs if( dialog == IM_NOTHING_SPECIAL ) @@ -1436,10 +1420,13 @@ LLUUID LLIMMgr::addSession( } else { - floater->openFloater(); + // *TODO: Remove this? Otherwise old communicate window opens on + // second initiation of IM session from People panel? + // floater->openFloater(); } //mTabContainer->selectTabPanel(panel); floater->setInputFocus(TRUE); + LLIMFloater::show(session_id); notifyObserverSessionAdded(floater->getSessionID(), name, other_participant_id); return floater->getSessionID(); } @@ -1768,35 +1755,8 @@ LLFloaterIMPanel* LLIMMgr::createFloater( const LLUUID& other_participant_id, const std::string& session_label, EInstantMessage dialog, - BOOL user_initiated) -{ - if (session_id.isNull()) - { - llwarns << "Creating LLFloaterIMPanel with null session ID" << llendl; - } - - llinfos << "LLIMMgr::createFloater: from " << other_participant_id - << " in session " << session_id << llendl; - std::vector<LLUUID> ids; - LLFloaterIMPanel* floater = new LLFloaterIMPanel(session_label, - session_id, - other_participant_id, - ids, - dialog); - LLTabContainer::eInsertionPoint i_pt = user_initiated ? LLTabContainer::RIGHT_OF_CURRENT : LLTabContainer::END; - LLFloaterChatterBox::getInstance()->addFloater(floater, FALSE, i_pt); - mFloaters.insert(floater->getHandle()); - LLIMModel::instance().newSession(session_id, session_label, dialog, other_participant_id); - return floater; -} - -LLFloaterIMPanel* LLIMMgr::createFloater( - const LLUUID& session_id, - const LLUUID& other_participant_id, - const std::string& session_label, - const LLDynamicArray<LLUUID>& ids, - EInstantMessage dialog, - BOOL user_initiated) + BOOL user_initiated, + const LLDynamicArray<LLUUID>& ids) { if (session_id.isNull()) { diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index b3b821f2ac..777d68978e 100644 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -71,7 +71,7 @@ public: bool newSession(LLUUID session_id, std::string name, EInstantMessage type, LLUUID other_participant_id); std::list<LLSD> getMessages(LLUUID session_id, int start_index = 0); - bool addMessage(LLUUID session_id, std::string from, std::string utf8_text); + bool addMessage(LLUUID session_id, std::string from, LLUUID other_participant_id, std::string utf8_text); bool addToHistory(LLUUID session_id, std::string from, std::string utf8_text); //used to get the name of the session, for use as the title //currently just the other avatar name @@ -189,6 +189,9 @@ public: // IM received that you haven't seen yet BOOL getIMReceived() const; + // Calc number of unread IMs + S32 getNumberOfUnreadIM(); + // This method is used to go through all active sessions and // disable all of them. This method is usally called when you are // forced to log out or similar situations where you do not have a @@ -231,14 +234,8 @@ private: const LLUUID& target_id, const std::string& name, EInstantMessage dialog, - BOOL user_initiated = FALSE); - - LLFloaterIMPanel* createFloater(const LLUUID& session_id, - const LLUUID& target_id, - const std::string& name, - const LLDynamicArray<LLUUID>& ids, - EInstantMessage dialog, - BOOL user_initiated = FALSE); + BOOL user_initiated = FALSE, + const LLDynamicArray<LLUUID>& ids = LLDynamicArray<LLUUID>()); // This simple method just iterates through all of the ids, and // prints a simple message if they are not online. Used to help diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 5f634496d3..efa97de692 100644 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -155,6 +155,9 @@ std::string ICON_NAME[ICON_NAME_COUNT] = "inv_item_animation.tga", "inv_item_gesture.tga", + + "inv_item_linkitem.tga", + "inv_item_linkfolder.tga" }; BOOL gAddToOutfit = FALSE; @@ -1655,15 +1658,6 @@ BOOL move_inv_category_world_to_agent(const LLUUID& object_id, return accept; } -class LLFindWearables : public LLInventoryCollectFunctor -{ -public: - LLFindWearables() {} - virtual ~LLFindWearables() {} - virtual bool operator()(LLInventoryCategory* cat, - LLInventoryItem* item); -}; - bool LLFindWearables::operator()(LLInventoryCategory* cat, LLInventoryItem* item) { @@ -2309,8 +2303,6 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags) else if(isAgentInventory()) // do not allow creating in library { mItems.push_back(std::string("New Folder")); - mItems.push_back(std::string("New Outfit")); - mItems.push_back(std::string("New My Outfits")); mItems.push_back(std::string("New Script")); mItems.push_back(std::string("New Note")); mItems.push_back(std::string("New Gesture")); @@ -3692,8 +3684,8 @@ void LLObjectBridge::buildContextMenu(LLMenuGL& menu, U32 flags) // commented out for DEV-32347 //items.push_back(std::string("Restore to Last Position")); - LLMenuGL* attach_menu = menu.getChildMenuByName("Attach To", TRUE); - LLMenuGL* attach_hud_menu = menu.getChildMenuByName("Attach To HUD", TRUE); + LLMenuGL* attach_menu = menu.findChildMenuByName("Attach To", TRUE); + LLMenuGL* attach_hud_menu = menu.findChildMenuByName("Attach To HUD", TRUE); LLVOAvatar *avatarp = gAgent.getAvatarObject(); if (attach_menu && (attach_menu->getChildCount() == 0) @@ -4157,6 +4149,58 @@ void wear_inventory_category_on_avatar( LLInventoryCategory* category, BOOL appe } } +void removeDuplicateItems(LLInventoryModel::item_array_t& dst, const LLInventoryModel::item_array_t& src) +{ + LLInventoryModel::item_array_t new_dst; + std::set<LLUUID> mark_inventory; + std::set<LLUUID> mark_asset; + + S32 inventory_dups = 0; + S32 asset_dups = 0; + + for (LLInventoryModel::item_array_t::const_iterator src_pos = src.begin(); + src_pos != src.end(); + ++src_pos) + { + LLUUID src_item_id = (*src_pos)->getLinkedUUID(); + mark_inventory.insert(src_item_id); + LLUUID src_asset_id = (*src_pos)->getAssetUUID(); + mark_asset.insert(src_asset_id); + } + + for (LLInventoryModel::item_array_t::const_iterator dst_pos = dst.begin(); + dst_pos != dst.end(); + ++dst_pos) + { + LLUUID dst_item_id = (*dst_pos)->getLinkedUUID(); + + if (mark_inventory.find(dst_item_id) == mark_inventory.end()) + { + } + else + { + inventory_dups++; + } + + LLUUID dst_asset_id = (*dst_pos)->getAssetUUID(); + + if (mark_asset.find(dst_asset_id) == mark_asset.end()) + { + // Item is not already present in COF. + new_dst.put(*dst_pos); + mark_asset.insert(dst_item_id); + } + else + { + asset_dups++; + } + } + llinfos << "removeDups, original " << dst.count() << " final " << new_dst.count() + << " inventory dups " << inventory_dups << " asset_dups " << asset_dups << llendl; + + dst = new_dst; +} + void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOOL append, BOOL follow_folder_links ) { // Find all the wearables that are in the category's subtree. @@ -4205,22 +4249,35 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO } const LLUUID ¤t_outfit_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_CURRENT_OUTFIT); + // Processes that take time should show the busy cursor + inc_busy_count(); + + LLInventoryModel::cat_array_t co_cat_array; + LLInventoryModel::item_array_t co_item_array; + gInventory.collectDescendents(current_outfit_id, co_cat_array, co_item_array, + LLInventoryModel::EXCLUDE_TRASH); + if (append) + { + // Remove duplicates and update counts. + removeDuplicateItems(item_array,co_item_array); + wearable_count = item_array.count(); + + removeDuplicateItems(obj_item_array,co_item_array); + obj_count = obj_item_array.count(); + + removeDuplicateItems(gest_item_array,co_item_array); + gest_count = gest_item_array.count(); + } + if (wearable_count > 0 || obj_count > 0) { - // Processes that take time should show the busy cursor - inc_busy_count(); - - // Remove all current outfit folder links if we're now replacing the contents. - if (!append) + if (!append) { - LLInventoryModel::cat_array_t cat_array; - LLInventoryModel::item_array_t item_array; - gInventory.collectDescendents(current_outfit_id, cat_array, item_array, - LLInventoryModel::EXCLUDE_TRASH); - for (i = 0; i < item_array.count(); ++i) + // Remove all current outfit folder links if we're now replacing the contents. + for (i = 0; i < co_item_array.count(); ++i) { - gInventory.purgeObject(item_array.get(i)->getUUID()); + gInventory.purgeObject(co_item_array.get(i)->getUUID()); } } } @@ -4272,10 +4329,10 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO // Fetch the wearables about to be worn. LLWearableList::instance().getAsset(found->mAssetID, - found->mName, - found->mAssetType, - wear_inventory_category_on_avatar_loop, - (void*)holder); + found->mName, + found->mAssetType, + wear_inventory_category_on_avatar_loop, + (void*)holder); } } @@ -4352,9 +4409,10 @@ void wear_inventory_category_on_avatar_step2( BOOL proceed, LLUUID category, BOO } } - // Create a link to the folder that we wore. + // In the particular case that we're switching to a different outfit, + // create a link to the folder that we wore. LLViewerInventoryCategory* catp = gInventory.getCategory(category); - if (catp) + if (!append && catp && catp->getPreferredType() == LLAssetType::AT_OUTFIT) { link_inventory_item(gAgent.getID(), category, current_outfit_id, catp->getName(), LLAssetType::AT_LINK_FOLDER, LLPointer<LLInventoryCallback>(NULL)); @@ -5240,6 +5298,10 @@ std::string LLLinkItemBridge::sPrefix("Link: "); LLUIImagePtr LLLinkItemBridge::getIcon() const { + if (LLViewerInventoryItem *item = getItem()) + { + return get_item_icon(item->getActualType(), LLInventoryType::IT_NONE, 0, FALSE); + } return get_item_icon(LLAssetType::AT_LINK, LLInventoryType::IT_NONE, 0, FALSE); } @@ -5345,6 +5407,7 @@ void LLLinkFolderBridge::gotoItem(LLFolderView *folder) } base_folder->setOpen(TRUE); folder->setSelectionFromRoot(base_folder,TRUE); + folder->scrollToShowSelection(); } } } diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index 5cfebe6c15..3af54c52ea 100644 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -76,6 +76,9 @@ enum EInventoryIcon ANIMATION_ICON_NAME, GESTURE_ICON_NAME, + LINKITEM_ICON_NAME, + LINKFOLDER_ICON_NAME, + ICON_NAME_COUNT }; diff --git a/indra/newview/llinventoryfilter.cpp b/indra/newview/llinventoryfilter.cpp index 596211f16c..c41900d691 100644 --- a/indra/newview/llinventoryfilter.cpp +++ b/indra/newview/llinventoryfilter.cpp @@ -111,7 +111,15 @@ BOOL LLInventoryFilter::check(LLFolderViewItem* item) } else { - passed_type |= ((1LL << listener->getInventoryType() & mFilterOps.mFilterTypes) != U64(0)) || listener->getInventoryType() == LLInventoryType::IT_NONE; + passed_type |= ((1LL << listener->getInventoryType() & mFilterOps.mFilterTypes) != U64(0)); + if (listener->getInventoryType() == LLInventoryType::IT_NONE) + { + const LLInventoryObject *obj = gInventory.getObject(listener->getUUID()); + if (obj && !obj->getIsLinkType()) + { + passed_type = TRUE; + } + } } BOOL passed = passed_type diff --git a/indra/newview/llinventorymodel.cpp b/indra/newview/llinventorymodel.cpp index 6e89fd75e9..ef2ba5c11b 100644 --- a/indra/newview/llinventorymodel.cpp +++ b/indra/newview/llinventorymodel.cpp @@ -1786,6 +1786,12 @@ void LLInventoryModel::addItem(LLViewerInventoryItem* item) //llinfos << "LLInventoryModel::addItem()" << llendl; if(item) { + // This condition means that we tried to add a link without the baseobj being in memory. + // The item will show up as a broken link. + if (item->getIsBrokenLink()) + { + llwarns << "Add link item without baseobj present ( name: " << item->getName() << " itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) parent: " << item->getParentUUID() << llendl; + } mItemMap[item->getUUID()] = item; //mInventory[item->getUUID()] = item; } @@ -2028,6 +2034,7 @@ bool LLInventoryModel::loadSkeleton( update_map_t child_counts; cat_array_t categories; item_array_t items; + cat_set_t invalid_categories; // Used to mark categories that weren't successfully loaded. std::string owner_id_str; owner_id.toString(owner_id_str); std::string path(gDirUtilp->getExpandedFilename(LL_PATH_CACHE, owner_id_str)); @@ -2093,7 +2100,7 @@ bool LLInventoryModel::loadSkeleton( } // go ahead and add the cats returned during the download - std::set<LLUUID>::iterator not_cached_id = cached_ids.end(); + std::set<LLUUID>::const_iterator not_cached_id = cached_ids.end(); cached_category_count = cached_ids.size(); for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it) { @@ -2113,16 +2120,26 @@ bool LLInventoryModel::loadSkeleton( // category with a correctly cached parent count = items.count(); cat_map_t::iterator unparented = mCategoryMap.end(); - for(int i = 0; i < count; ++i) + for(item_array_t::const_iterator item_iter = items.begin(); + item_iter != items.end(); + ++item_iter) { - cat_map_t::iterator cit = mCategoryMap.find(items[i]->getParentUUID()); + LLViewerInventoryItem *item = (*item_iter).get(); + const cat_map_t::iterator cit = mCategoryMap.find(item->getParentUUID()); if(cit != unparented) { - LLViewerInventoryCategory* cat = cit->second; + const LLViewerInventoryCategory* cat = cit->second.get(); if(cat->getVersion() != NO_VERSION) { - addItem(items[i]); + // This can happen if the linked object's baseobj is removed from the cache but the linked object is still in the cache. + if (item->getIsBrokenLink()) + { + llinfos << "Attempted to cached link item without baseobj present ( itemID: " << item->getUUID() << " assetID: " << item->getAssetUUID() << " ) " << llendl; + invalid_categories.insert(cit->second); + continue; + } + addItem(item); cached_item_count += 1; ++child_counts[cat->getUUID()]; } @@ -2144,14 +2161,13 @@ bool LLInventoryModel::loadSkeleton( // At this point, we need to set the known descendents for each // category which successfully cached so that we do not // needlessly fetch descendents for categories which we have. - update_map_t::iterator no_child_counts = child_counts.end(); - update_map_t::iterator the_count; + update_map_t::const_iterator no_child_counts = child_counts.end(); for(cat_set_t::iterator it = temp_cats.begin(); it != temp_cats.end(); ++it) { - LLViewerInventoryCategory* cat = (*it); + LLViewerInventoryCategory* cat = (*it).get(); if(cat->getVersion() != NO_VERSION) { - the_count = child_counts.find(cat->getUUID()); + update_map_t::const_iterator the_count = child_counts.find(cat->getUUID()); if(the_count != no_child_counts) { cat->setDescendentCount((*the_count).second.mValue); @@ -2163,6 +2179,17 @@ bool LLInventoryModel::loadSkeleton( } } + // Invalidate all categories that failed fetching descendents for whatever + // reason (e.g. one of the descendents was a broken link). + for (cat_set_t::iterator invalid_cat_it = invalid_categories.begin(); + invalid_cat_it != invalid_categories.end(); + invalid_cat_it++) + { + LLViewerInventoryCategory* cat = (*invalid_cat_it).get(); + cat->setVersion(NO_VERSION); + llinfos << "Invalidating category name: " << cat->getName() << " UUID: " << cat->getUUID() << " due to invalid descendents cache" << llendl; + } + if(remove_inventory_file) { // clean up the gunzipped file. diff --git a/indra/newview/llinventorymodel.h b/indra/newview/llinventorymodel.h index bd0aaa3678..649a7e5b16 100644 --- a/indra/newview/llinventorymodel.h +++ b/indra/newview/llinventorymodel.h @@ -724,6 +724,20 @@ protected: std::string mName; }; +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Class LLFindWearables +// +// Collects wearables based on item type. +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class LLFindWearables : public LLInventoryCollectFunctor +{ +public: + LLFindWearables() {} + virtual ~LLFindWearables() {} + virtual bool operator()(LLInventoryCategory* cat, + LLInventoryItem* item); +}; + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Class LLInventoryCompletionObserver diff --git a/indra/newview/lllandmarkactions.cpp b/indra/newview/lllandmarkactions.cpp index b51064f226..2c56a70ced 100644 --- a/indra/newview/lllandmarkactions.cpp +++ b/indra/newview/lllandmarkactions.cpp @@ -33,16 +33,24 @@ #include "llviewerprecompiledheaders.h" #include "lllandmarkactions.h" -#include "llagent.h" +#include "roles_constants.h" + #include "llinventory.h" -#include "llinventorymodel.h" #include "lllandmark.h" -#include "lllandmarklist.h" -#include "llnotifications.h" #include "llparcel.h" + +#include "llnotifications.h" + +#include "llagent.h" +#include "llinventorymodel.h" +#include "lllandmarklist.h" +#include "llslurl.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" -#include "roles_constants.h" +#include "llworldmap.h" +#include "lllandmark.h" +#include "llinventorymodel.h" +#include "llagentui.h" // Returns true if the given inventory item is a landmark pointing to the current parcel. // Used to filter inventory items. @@ -66,9 +74,81 @@ public: } }; +class LLFetchLandmarksByName : public LLInventoryCollectFunctor +{ +private: + std::string name; + BOOL use_substring; + //this member will be contain copy of founded items to keep the result unique + std::set<std::string> check_duplicate; + +public: +LLFetchLandmarksByName(std::string &landmark_name, BOOL if_use_substring) +:name(landmark_name), +use_substring(if_use_substring) + { + LLStringUtil::toLower(name); + } + +public: + /*virtual*/ bool operator()(LLInventoryCategory* cat, LLInventoryItem* item) + { + if (!item || item->getType() != LLAssetType::AT_LANDMARK) + return false; + + LLLandmark* landmark = gLandmarkList.getAsset(item->getAssetUUID()); + if (!landmark) // the landmark not been loaded yet + return false; + + bool acceptable = false; + std::string landmark_name = item->getName(); + LLStringUtil::toLower(landmark_name); + if(use_substring) + { + acceptable = landmark_name.find( name ) != std::string::npos; + } + else + { + acceptable = landmark_name == name; + } + if(acceptable){ + if(check_duplicate.find(landmark_name) != check_duplicate.end()){ + // we have duplicated items in landmarks + acceptable = false; + }else{ + check_duplicate.insert(landmark_name); + } + } + + return acceptable; + } +}; + +LLInventoryModel::item_array_t LLLandmarkActions::fetchLandmarksByName(std::string& name, BOOL use_substring) +{ + LLInventoryModel::cat_array_t cats; + LLInventoryModel::item_array_t items; + LLFetchLandmarksByName fetchLandmarks(name, use_substring); + gInventory.collectDescendentsIf(gInventory.getRootFolderID(), + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + fetchLandmarks); + return items; +} + bool LLLandmarkActions::landmarkAlreadyExists() { // Determine whether there are landmarks pointing to the current parcel. + LLInventoryModel::item_array_t items; + collectParcelLandmark(items); + return !items.empty(); +} + + +LLViewerInventoryItem* LLLandmarkActions::findLandmarkForAgentParcel() +{ + // Determine whether there are landmarks pointing to the current parcel. LLInventoryModel::cat_array_t cats; LLInventoryModel::item_array_t items; LLIsAgentParcelLandmark is_current_parcel_landmark; @@ -78,7 +158,12 @@ bool LLLandmarkActions::landmarkAlreadyExists() LLInventoryModel::EXCLUDE_TRASH, is_current_parcel_landmark); - return !items.empty(); + if(items.empty()) + { + return NULL; + } + + return items[0]; } bool LLLandmarkActions::canCreateLandmarkHere() @@ -133,9 +218,82 @@ void LLLandmarkActions::createLandmarkHere() { std::string landmark_name, landmark_desc; - gAgent.buildLocationString(landmark_name, LLAgent::LOCATION_FORMAT_LANDMARK); - gAgent.buildLocationString(landmark_desc, LLAgent::LOCATION_FORMAT_FULL); + LLAgentUI::buildLocationString(landmark_name, LLAgent::LOCATION_FORMAT_LANDMARK); + LLAgentUI::buildLocationString(landmark_desc, LLAgent::LOCATION_FORMAT_FULL); LLUUID folder_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK); createLandmarkHere(landmark_name, landmark_desc, folder_id); } + +void LLLandmarkActions::getSLURLfromPosGlobal(const LLVector3d& global_pos, slurl_callback_t cb, bool escaped /* = true */) +{ + std::string sim_name; + bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name); + if (gotSimName) + { + std::string slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); + cb(slurl); + + return; + } + else + { + U64 new_region_handle = to_region_handle(global_pos); + + LLWorldMap::url_callback_t url_cb = boost::bind(&LLLandmarkActions::onRegionResponse, + cb, + global_pos, + escaped, + _1, _2, _3, _4); + + LLWorldMap::getInstance()->sendHandleRegionRequest(new_region_handle, url_cb, std::string("unused"), false); + } +} + +void LLLandmarkActions::onRegionResponse(slurl_callback_t cb, + const LLVector3d& global_pos, + bool escaped, + U64 region_handle, + const std::string& url, + const LLUUID& snapshot_id, + bool teleport) +{ + std::string sim_name; + std::string slurl; + bool gotSimName = LLWorldMap::getInstance()->simNameFromPosGlobal(global_pos, sim_name); + if (gotSimName) + { + slurl = LLSLURL::buildSLURLfromPosGlobal(sim_name, global_pos, escaped); + } + else + { + slurl = ""; + } + + cb(slurl); +} + +bool LLLandmarkActions::getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal) +{ + LLViewerInventoryItem* item = gInventory.getItem(landmarkInventoryItemID); + if (NULL == item) + return false; + + const LLUUID& asset_id = item->getAssetUUID(); + LLLandmark* landmark = gLandmarkList.getAsset(asset_id, NULL); + + if (NULL == landmark) + return false; + + return landmark->getGlobalPos(posGlobal); +} + +void LLLandmarkActions::collectParcelLandmark(LLInventoryModel::item_array_t& items){ + LLInventoryModel::cat_array_t cats; + LLIsAgentParcelLandmark is_current_parcel_landmark; + gInventory.collectDescendentsIf(gInventory.getRootFolderID(), + cats, + items, + LLInventoryModel::EXCLUDE_TRASH, + is_current_parcel_landmark); +} diff --git a/indra/newview/lllandmarkactions.h b/indra/newview/lllandmarkactions.h index e1e94edb75..c74072c0f4 100644 --- a/indra/newview/lllandmarkactions.h +++ b/indra/newview/lllandmarkactions.h @@ -1,68 +1,116 @@ /** -* @file lllandmarkactions.h -* @brief LLLandmark class declaration -* -* $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$ -*/ + * @file lllandmarkactions.h + * @brief LLLandmark class declaration + * + * $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_LLLANDMARKACTIONS_H #define LL_LLLANDMARKACTIONS_H +#include "llinventorymodel.h" + /** -* @brief Provides helper functions to manage landmarks -*/ + * @brief Provides helper functions to manage landmarks + */ class LLLandmarkActions { public: + typedef boost::function<void(std::string& slurl)> slurl_callback_t; /** - * @brief Checks whether landmark exists for current parcel. - */ + * @brief Fetches landmark LLViewerInventoryItems for the given landmark name. + */ + static LLInventoryModel::item_array_t fetchLandmarksByName(std::string& name, BOOL if_use_substring); + /** + * @brief Checks whether landmark exists for current parcel. + */ static bool landmarkAlreadyExists(); /** - * @brief Checks whether agent has rights to create landmark for current parcel. - */ + * @brief Searches landmark for parcel agent is currently in. + * @return Returns landmark for agent parcel or NULL. + * + * *TODO: dzaporozhan: There can be many landmarks for single parcel. + */ + static LLViewerInventoryItem* findLandmarkForAgentParcel(); + + /** + * @brief Checks whether agent has rights to create landmark for current parcel. + */ static bool canCreateLandmarkHere(); /** - * @brief Creates landmark for current parcel. - */ + * @brief Creates landmark for current parcel. + */ static void createLandmarkHere(); /** - * @brief Creates landmark for current parcel. - */ + * @brief Creates landmark for current parcel. + */ static void createLandmarkHere( const std::string& name, const std::string& desc, const LLUUID& folder_id); + /** + * @brief Trying to find in inventory a landmark of the current parcel. + * Normally items should contain only one item, + * because we can create the only landmark per parcel according to Navigation spec. + */ + static void collectParcelLandmark(LLInventoryModel::item_array_t& items); + + /** + * @brief Creates SLURL for given global position. + */ + static void getSLURLfromPosGlobal(const LLVector3d& global_pos, slurl_callback_t cb, bool escaped = true); + + /** + * @brief Gets landmark global position specified by inventory LLUUID. + * Found position is placed into "posGlobal" variable. + *. + * @return - true if specified item exists in Inventory and an appropriate landmark found. + * and its position is known, false otherwise. + */ + // *TODO: mantipov: profide callback for cases, when Landmark is not loaded yet. + static bool getLandmarkGlobalPos(const LLUUID& landmarkInventoryItemID, LLVector3d& posGlobal); + +private: + LLLandmarkActions(); + LLLandmarkActions(const LLLandmarkActions&); + + static void onRegionResponse(slurl_callback_t cb, + const LLVector3d& global_pos, + bool escaped, + U64 region_handle, + const std::string& url, + const LLUUID& snapshot_id, + bool teleport); }; #endif //LL_LLLANDMARKACTIONS_H diff --git a/indra/newview/lllocationhistory.cpp b/indra/newview/lllocationhistory.cpp index 03d6953521..c83cde9d83 100644 --- a/indra/newview/lllocationhistory.cpp +++ b/indra/newview/lllocationhistory.cpp @@ -49,8 +49,15 @@ void LLLocationHistory::addItem(const std::string & item, const std::string & to static LLUICachedControl<S32> max_items("LocationHistoryMaxSize", 100); // check if this item doesn't duplicate any existing one - if (touchItem(item)) { - return; + std::vector<std::string>::iterator item_iter = std::find_if(mItems.begin(), mItems.end(), + boost::bind(&LLLocationHistory::equalByRegionParcel,this,_1,item)); + if(item_iter != mItems.end()){ + /*replace duplicate. + * If an item's region and item's parcel are equal. + */ + mToolTips.erase(*item_iter); + mItems.erase(item_iter); + } mItems.push_back(item); @@ -65,6 +72,21 @@ void LLLocationHistory::addItem(const std::string & item, const std::string & to } } +/** + * check if the history item is equal. + * @return true - if region name and parcel is equal. + */ +bool LLLocationHistory::equalByRegionParcel(const std::string& item, const std::string& newItem){ + + + S32 itemIndex = item.find('('); + S32 newItemIndex = newItem.find('('); + + std::string region_parcel = item.substr(0,itemIndex); + std::string new_region_parcel = newItem.substr(0,newItemIndex); + + return region_parcel == new_region_parcel; +} bool LLLocationHistory::touchItem(const std::string & item) { bool result = false; std::vector<std::string>::iterator item_iter = std::find(mItems.begin(), mItems.end(), item); @@ -135,7 +157,13 @@ void LLLocationHistory::save() const } for (location_list_t::const_iterator it = mItems.begin(); it != mItems.end(); ++it) - file << (*it) << delimiter << mToolTips.find(*it)->second << std::endl; + { + std::string tooltip = getToolTip(*it); + if(!tooltip.empty()) + { + file << (*it) << delimiter << tooltip << std::endl; + } + } file.close(); } diff --git a/indra/newview/lllocationhistory.h b/indra/newview/lllocationhistory.h index 67eabcdaca..060a6b2fe8 100644 --- a/indra/newview/lllocationhistory.h +++ b/indra/newview/lllocationhistory.h @@ -65,6 +65,7 @@ public: void dump() const; private: + bool equalByRegionParcel(const std::string& item, const std::string& item_to_add); const static char delimiter; std::vector<std::string> mItems; std::map<std::string, std::string> mToolTips; diff --git a/indra/newview/lllocationinputctrl.cpp b/indra/newview/lllocationinputctrl.cpp index e2dc7d69a1..1542c7483a 100644 --- a/indra/newview/lllocationinputctrl.cpp +++ b/indra/newview/lllocationinputctrl.cpp @@ -37,25 +37,28 @@ // common includes #include "llbutton.h" -#include "llfloaterreg.h" #include "llfocusmgr.h" -#include "llkeyboard.h" +#include "llmenugl.h" #include "llstring.h" +#include "lltrans.h" #include "lluictrlfactory.h" -#include "v2math.h" // newview includes #include "llagent.h" -#include "llfloaterland.h" #include "llinventorymodel.h" #include "lllandmarkactions.h" #include "lllandmarklist.h" #include "lllocationhistory.h" #include "llsidetray.h" +#include "llslurl.h" +#include "lltrans.h" #include "llviewerinventory.h" #include "llviewerparcelmgr.h" #include "llviewercontrol.h" +#include "llviewermenu.h" #include "llurllineeditorctrl.h" +#include "llagentui.h" + //============================================================================ /* * "ADD LANDMARK" BUTTON UPDATING LOGIC @@ -151,6 +154,8 @@ static LLDefaultChildRegistry::Register<LLLocationInputCtrl> r("location_input") LLLocationInputCtrl::Params::Params() : add_landmark_image_enabled("add_landmark_image_enabled"), add_landmark_image_disabled("add_landmark_image_disabled"), + add_landmark_image_hover("add_landmark_image_hover"), + add_landmark_image_selected("add_landmark_image_selected"), add_landmark_button("add_landmark_button"), add_landmark_hpad("add_landmark_hpad", 0), info_button("info_button") @@ -161,7 +166,10 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) : LLComboBox(p), mAddLandmarkHPad(p.add_landmark_hpad), mInfoBtn(NULL), - mAddLandmarkBtn(NULL) + mLocationContextMenu(NULL), + mAddLandmarkBtn(NULL), + mLandmarkImageOn(NULL), + mLandmarkImageOff(NULL) { // Lets replace default LLLineEditor with LLLocationLineEditor // to make needed escaping while copying and cutting url @@ -197,30 +205,53 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) // "Add landmark" button. LLButton::Params al_params = p.add_landmark_button; + + // Image for unselected state will be set in updateAddLandmarkButton(), + // it will be either mLandmarkOn or mLandmarkOff if (p.add_landmark_image_enabled()) { - al_params.image_unselected = p.add_landmark_image_enabled; - al_params.image_selected = p.add_landmark_image_enabled; + mLandmarkImageOn = p.add_landmark_image_enabled; } if (p.add_landmark_image_disabled()) { - al_params.image_disabled = p.add_landmark_image_disabled; - al_params.image_disabled_selected = p.add_landmark_image_disabled; + mLandmarkImageOff = p.add_landmark_image_disabled; + } + + if(p.add_landmark_image_selected) + { + al_params.image_selected = p.add_landmark_image_selected; } + if (p.add_landmark_image_hover()) + { + al_params.image_hover_unselected = p.add_landmark_image_hover; + } + al_params.click_callback.function(boost::bind(&LLLocationInputCtrl::onAddLandmarkButtonClicked, this)); mAddLandmarkBtn = LLUICtrlFactory::create<LLButton>(al_params); enableAddLandmarkButton(true); addChild(mAddLandmarkBtn); + // Register callbacks and load the location field context menu (NB: the order matters). + LLUICtrl::CommitCallbackRegistry::currentRegistrar().add("Navbar.Action", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemClicked, this, _2)); + LLUICtrl::EnableCallbackRegistry::currentRegistrar().add("Navbar.EnableMenuItem", boost::bind(&LLLocationInputCtrl::onLocationContextMenuItemEnabled, this, _2)); + setPrearrangeCallback(boost::bind(&LLLocationInputCtrl::onLocationPrearrange, this, _2)); - getTextEntry()->setMouseUpCallback(boost::bind(&LLLocationInputCtrl::onTextEditorMouseUp, this, _2,_3,_4)); + getTextEntry()->setMouseUpCallback(boost::bind(&LLLocationInputCtrl::changeLocationPresentation, this)); + // Load the location field context menu + mLocationContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (!mLocationContextMenu) + { + llwarns << "Error loading navigation bar context menu" << llendl; + + } + getTextEntry()->setRightMouseUpCallback(boost::bind(&LLLocationInputCtrl::onTextEditorRightClicked,this,_2,_3,_4)); updateWidgetlayout(); // - Make the "Add landmark" button updated when either current parcel gets changed // or a landmark gets created or removed from the inventory. // - Update the location string on parcel change. - mParcelMgrConnection = LLViewerParcelMgr::getInstance()->setAgentParcelChangedCallback( + mParcelMgrConnection = LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback( boost::bind(&LLLocationInputCtrl::onAgentParcelChange, this)); mLocationHistoryConnection = LLLocationHistory::getInstance()->setLoadedCallback( @@ -230,6 +261,9 @@ LLLocationInputCtrl::LLLocationInputCtrl(const LLLocationInputCtrl::Params& p) mAddLandmarkObserver = new LLAddLandmarkObserver(this); gInventory.addObserver(mRemoveLandmarkObserver); gInventory.addObserver(mAddLandmarkObserver); + + mAddLandmarkTooltip = LLTrans::getString("location_ctrl_add_landmark"); + mEditLandmarkTooltip = LLTrans::getString("location_ctrl_edit_landmark"); } LLLocationInputCtrl::~LLLocationInputCtrl() @@ -376,7 +410,21 @@ void LLLocationInputCtrl::onInfoButtonClicked() void LLLocationInputCtrl::onAddLandmarkButtonClicked() { - LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); + LLViewerInventoryItem* landmark = LLLandmarkActions::findLandmarkForAgentParcel(); + + // Landmark exists, open it for preview and edit + if(landmark && landmark->getUUID().notNull()) + { + LLSD key; + key["type"] = "landmark"; + key["id"] = landmark->getUUID(); + + LLSideTray::getInstance()->showPanel("panel_places", key); + } + else + { + LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); + } } void LLLocationInputCtrl::onAgentParcelChange() @@ -399,13 +447,31 @@ void LLLocationInputCtrl::onLocationPrearrange(const LLSD& data) { std::string filter = data.asString(); rebuildLocationHistory(filter); + + //Let's add landmarks to the top of the list if any + if( filter.size() !=0 ) + { + LLInventoryModel::item_array_t landmark_items = LLLandmarkActions::fetchLandmarksByName(filter, TRUE); + + for(U32 i=0; i < landmark_items.size(); i++) + { + mList->addSimpleElement(landmark_items[i]->getName(), ADD_TOP); + } + } mList->mouseOverHighlightNthItem(-1); // Clear highlight on the last selected item. } -void LLLocationInputCtrl::onTextEditorMouseUp(S32 x, S32 y, MASK mask) + +void LLLocationInputCtrl::onTextEditorRightClicked(S32 x, S32 y, MASK mask) { - if (!mTextEntry->hasSelection()) { - setText(gAgent.getUnescapedSLURL()); - mTextEntry->selectAll(); + if (mLocationContextMenu) + { + updateContextMenu(); + mLocationContextMenu->buildDrawLabels(); + mLocationContextMenu->updateParent(LLMenuGL::sMenuContainer); + hideList(); + setFocus(true); + changeLocationPresentation(); + LLMenuGL::showPopup(this, mLocationContextMenu, x, y); } } @@ -429,11 +495,9 @@ void LLLocationInputCtrl::refreshLocation() // Update location field. std::string location_name; LLAgent::ELocationFormat format = (gSavedSettings.getBOOL("ShowCoordinatesOption") ? - LLAgent::LOCATION_FORMAT_FULL: LLAgent::LOCATION_FORMAT_NORMAL); - - if (!gAgent.buildLocationString(location_name,format)) - location_name = "Unknown"; + LLAgent::LOCATION_FORMAT_WITHOUT_SIM: LLAgent::LOCATION_FORMAT_NORMAL); + if (!LLAgentUI::buildLocationString(location_name, format)) location_name = "Unknown"; setText(location_name); } @@ -471,17 +535,49 @@ void LLLocationInputCtrl::focusTextEntry() void LLLocationInputCtrl::enableAddLandmarkButton(bool val) { - // Enable/disable the button. - mAddLandmarkBtn->setEnabled(val); + // We don't want to disable the button because it should be click able at any time, + // instead switch images. + LLUIImage* img = val ? mLandmarkImageOn : mLandmarkImageOff; + if(img) + { + mAddLandmarkBtn->setImageUnselected(img); + } } // Change the "Add landmark" button image // depending on whether current parcel has been landmarked. void LLLocationInputCtrl::updateAddLandmarkButton() { - enableAddLandmarkButton(!LLLandmarkActions::landmarkAlreadyExists()); + bool landmark_exists = LLLandmarkActions::landmarkAlreadyExists(); + enableAddLandmarkButton(!landmark_exists); + + std::string tooltip; + if(landmark_exists) + { + tooltip = mEditLandmarkTooltip; + } + else + { + tooltip = mAddLandmarkTooltip; + } + mAddLandmarkBtn->setToolTip(tooltip); } +void LLLocationInputCtrl::updateContextMenu(){ + + if (mLocationContextMenu) + { + LLMenuItemGL* landmarkItem = mLocationContextMenu->getChild<LLMenuItemGL>("Landmark"); + if (!LLLandmarkActions::landmarkAlreadyExists()) + { + landmarkItem->setLabel(LLTrans::getString("AddLandmarkNavBarMenu")); + } + else + { + landmarkItem->setLabel(LLTrans::getString("EditLandmarkNavBarMenu")); + } + } +} void LLLocationInputCtrl::updateWidgetlayout() { const LLRect& rect = getLocalRect(); @@ -503,3 +599,91 @@ void LLLocationInputCtrl::updateWidgetlayout() mAddLandmarkBtn->setRect(al_btn_rect); } } + +void LLLocationInputCtrl::changeLocationPresentation() +{ + //change location presentation only if user does not select anything and + //human-readable region name is being displayed + if(mTextEntry && !mTextEntry->hasSelection() && + !LLSLURL::isSLURL(mTextEntry->getText())) + { + //needs unescaped one + mTextEntry->setText(LLAgentUI::buildSLURL(false)); + mTextEntry->selectAll(); + } +} + +void LLLocationInputCtrl::onLocationContextMenuItemClicked(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == std::string("show_coordinates")) + { + gSavedSettings.setBOOL("ShowCoordinatesOption",!gSavedSettings.getBOOL("ShowCoordinatesOption")); + } + else if (item == std::string("landmark")) + { + LLInventoryModel::item_array_t items; + LLLandmarkActions::collectParcelLandmark(items); + + if(items.empty()) + { + LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); + }else{ + LLSideTray::getInstance()->showPanel("panel_places", + LLSD().insert("type", "landmark").insert("id",items.get(0)->getUUID())); + } + } + else if (item == std::string("cut")) + { + mTextEntry->cut(); + } + else if (item == std::string("copy")) + { + mTextEntry->copy(); + } + else if (item == std::string("paste")) + { + mTextEntry->paste(); + } + else if (item == std::string("delete")) + { + mTextEntry->deleteSelection(); + } + else if (item == std::string("select_all")) + { + mTextEntry->selectAll(); + } +} + +bool LLLocationInputCtrl::onLocationContextMenuItemEnabled(const LLSD& userdata) +{ + std::string item = userdata.asString(); + + if (item == std::string("can_cut")) + { + return mTextEntry->canCut(); + } + else if (item == std::string("can_copy")) + { + return mTextEntry->canCopy(); + } + else if (item == std::string("can_paste")) + { + return mTextEntry->canPaste(); + } + else if (item == std::string("can_delete")) + { + return mTextEntry->canDeselect(); + } + else if (item == std::string("can_select_all")) + { + return mTextEntry->canSelectAll(); + } + else if(item == std::string("show_coordinates")){ + + return gSavedSettings.getBOOL("ShowCoordinatesOption"); + } + + return false; +} diff --git a/indra/newview/lllocationinputctrl.h b/indra/newview/lllocationinputctrl.h index 0196aae4e7..d967df8257 100644 --- a/indra/newview/lllocationinputctrl.h +++ b/indra/newview/lllocationinputctrl.h @@ -40,6 +40,7 @@ class LLLandmark; // internals class LLAddLandmarkObserver; class LLRemoveLandmarkObserver; +class LLMenuGL; /** * Location input control. @@ -58,7 +59,9 @@ public: : public LLInitParam::Block<Params, LLComboBox::Params> { Optional<LLUIImage*> add_landmark_image_enabled, - add_landmark_image_disabled; + add_landmark_image_disabled, + add_landmark_image_hover, + add_landmark_image_selected; Optional<S32> add_landmark_hpad; Optional<LLButton::Params> add_landmark_button, info_button; @@ -92,22 +95,32 @@ private: virtual ~LLLocationInputCtrl(); void focusTextEntry(); + /** + * Changes the "Add landmark" button image + * depending on whether current parcel has been landmarked. + */ void enableAddLandmarkButton(bool val); void refresh(); void refreshLocation(); void rebuildLocationHistory(std::string filter = ""); void setText(const LLStringExplicit& text); void updateAddLandmarkButton(); + void updateContextMenu(); void updateWidgetlayout(); + void changeLocationPresentation(); void onInfoButtonClicked(); void onLocationHistoryLoaded(); void onLocationPrearrange(const LLSD& data); - void onTextEditorMouseUp(S32 x, S32 y, MASK mask); + void onTextEditorRightClicked(S32 x, S32 y, MASK mask); void onLandmarkLoaded(LLLandmark* lm); void onAddLandmarkButtonClicked(); void onAgentParcelChange(); + // callbacks + bool onLocationContextMenuItemEnabled(const LLSD& userdata); + void onLocationContextMenuItemClicked(const LLSD& userdata); + LLMenuGL* mLocationContextMenu; LLButton* mAddLandmarkBtn; LLButton* mInfoBtn; S32 mAddLandmarkHPad; @@ -117,6 +130,11 @@ private: boost::signals2::connection mParcelMgrConnection; boost::signals2::connection mLocationHistoryConnection; + LLUIImage* mLandmarkImageOn; + LLUIImage* mLandmarkImageOff; + + std::string mAddLandmarkTooltip; + std::string mEditLandmarkTooltip; }; #endif diff --git a/indra/newview/llloginhandler.cpp b/indra/newview/llloginhandler.cpp index 554163c8e1..6f0b8a3c1e 100644 --- a/indra/newview/llloginhandler.cpp +++ b/indra/newview/llloginhandler.cpp @@ -164,7 +164,7 @@ void LLLoginHandler::parse(const LLSD& queryMap) bool LLLoginHandler::handle(const LLSD& tokens, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { parse(query_map); diff --git a/indra/newview/llloginhandler.h b/indra/newview/llloginhandler.h index c76d7e8274..0844b80c7c 100644 --- a/indra/newview/llloginhandler.h +++ b/indra/newview/llloginhandler.h @@ -40,7 +40,7 @@ class LLLoginHandler : public LLCommandHandler public: // allow from external browsers LLLoginHandler() : LLCommandHandler("login", false) { } - /*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, LLWebBrowserCtrl* web); + /*virtual*/ bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web); // Fill in our internal fields from a SLURL like // secondlife:///app/login?first=Bob&last=Dobbs diff --git a/indra/newview/llmediactrl.cpp b/indra/newview/llmediactrl.cpp new file mode 100644 index 0000000000..62b38f2b4a --- /dev/null +++ b/indra/newview/llmediactrl.cpp @@ -0,0 +1,937 @@ +/** + * @file LLMediaCtrl.cpp + * @brief Web browser UI control + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-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 "llviewerprecompiledheaders.h" + + +#include "llmediactrl.h" + +// viewer includes +#include "llfloaterhtml.h" +#include "llfloaterworldmap.h" +#include "lluictrlfactory.h" +#include "llurldispatcher.h" +#include "llurlsimstring.h" +#include "llviewborder.h" +#include "llviewercontrol.h" +#include "llviewermedia.h" +#include "llviewerwindow.h" +#include "llnotifications.h" +#include "llweb.h" +#include "llrender.h" +#include "llpluginclassmedia.h" +#include "llslurl.h" +#include "lluictrlfactory.h" // LLDefaultChildRegistry + +// linden library includes +#include "llfocusmgr.h" + +extern BOOL gRestoreGL; + +static LLDefaultChildRegistry::Register<LLMediaCtrl> r("web_browser"); + +LLMediaCtrl::Params::Params() +: start_url("start_url"), + border_visible("border_visible", true), + ignore_ui_scale("ignore_ui_scale", true), + hide_loading("hide_loading", false), + caret_color("caret_color") +{} + +LLMediaCtrl::LLMediaCtrl( const Params& p) : + LLPanel( p ), + mTextureDepthBytes( 4 ), + mBorder(NULL), + mFrequentUpdates( true ), + mForceUpdate( false ), + mOpenLinksInExternalBrowser( false ), + mOpenLinksInInternalBrowser( false ), + mTrusted( false ), + mHomePageUrl( "" ), + mIgnoreUIScale( true ), + mAlwaysRefresh( false ), + mExternalUrl( "" ), + mMediaSource( 0 ), + mTakeFocusOnClick( true ), + mCurrentNavUrl( "" ), + mLastSetCursor( UI_CURSOR_ARROW ), + mStretchToFill( true ), + mMaintainAspectRatio ( true ), + mHideLoading (false) +{ + { + LLColor4 color = p.caret_color().get(); + setCaretColor( (unsigned int)color.mV[0], (unsigned int)color.mV[1], (unsigned int)color.mV[2] ); + } + + setIgnoreUIScale(p.ignore_ui_scale()); + + setHomePageUrl(p.start_url()); + + setBorderVisible(p.border_visible()); + + mHideLoading = p.hide_loading(); + + S32 screen_width = mIgnoreUIScale ? + llround((F32)getRect().getWidth() * LLUI::sGLScaleFactor.mV[VX]) : getRect().getWidth(); + S32 screen_height = mIgnoreUIScale ? + llround((F32)getRect().getHeight() * LLUI::sGLScaleFactor.mV[VY]) : getRect().getHeight(); + + mMediaTextureID.generate(); + mMediaSource = LLViewerMedia::newMediaImpl(mHomePageUrl, mMediaTextureID, screen_width, screen_height, false, false, "text/html"); + if ( !mMediaSource ) + { + llwarns << "media source create failed " << llendl; + // return; + } + + mMediaSource->setVisible( getVisible() ); + + mMediaSource->addObserver( this ); + + // FIXME: How do we create a bevel now? +// LLRect border_rect( 0, getRect().getHeight() + 2, getRect().getWidth() + 2, 0 ); +// mBorder = new LLViewBorder( std::string("web control border"), border_rect, LLViewBorder::BEVEL_IN ); +// addChild( mBorder ); +} + +//////////////////////////////////////////////////////////////////////////////// +// note: this is now a singleton and destruction happens via initClass() now +LLMediaCtrl::~LLMediaCtrl() +{ + + if (mMediaSource) + { + mMediaSource->remObserver( this ); + mMediaSource = NULL; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::setBorderVisible( BOOL border_visible ) +{ + if ( mBorder ) + { + mBorder->setVisible( border_visible ); + }; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::setTakeFocusOnClick( bool take_focus ) +{ + mTakeFocusOnClick = take_focus; +} + +//////////////////////////////////////////////////////////////////////////////// +// set flag that forces the embedded browser to open links in the external system browser +void LLMediaCtrl::setOpenInExternalBrowser( bool valIn ) +{ + mOpenLinksInExternalBrowser = valIn; +}; + +//////////////////////////////////////////////////////////////////////////////// +// set flag that forces the embedded browser to open links in the internal browser floater +void LLMediaCtrl::setOpenInInternalBrowser( bool valIn ) +{ + mOpenLinksInInternalBrowser = valIn; +}; + +//////////////////////////////////////////////////////////////////////////////// +void LLMediaCtrl::setTrusted( bool valIn ) +{ + mTrusted = valIn; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::handleHover( S32 x, S32 y, MASK mask ) +{ + convertInputCoords(x, y); + + if (mMediaSource) + mMediaSource->mouseMove(x, y); + + gViewerWindow->setCursor(mLastSetCursor); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::handleScrollWheel( S32 x, S32 y, S32 clicks ) +{ + if (mMediaSource && mMediaSource->hasMedia()) + mMediaSource->getMediaPlugin()->scrollEvent(0, clicks, MASK_NONE); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::handleMouseUp( S32 x, S32 y, MASK mask ) +{ + convertInputCoords(x, y); + + if (mMediaSource) + { + mMediaSource->mouseUp(x, y); + + // *HACK: LLMediaImplLLMozLib automatically takes focus on mouseup, + // in addition to the onFocusReceived() call below. Undo this. JC + if (!mTakeFocusOnClick) + { + mMediaSource->focus(false); + gViewerWindow->focusClient(); + } + } + + gFocusMgr.setMouseCapture( NULL ); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::handleMouseDown( S32 x, S32 y, MASK mask ) +{ + convertInputCoords(x, y); + + if (mMediaSource) + mMediaSource->mouseDown(x, y); + + gFocusMgr.setMouseCapture( this ); + + if (mTakeFocusOnClick) + { + setFocus( TRUE ); + } + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::handleDoubleClick( S32 x, S32 y, MASK mask ) +{ + convertInputCoords(x, y); + + if (mMediaSource) + mMediaSource->mouseLeftDoubleClick( x, y ); + + gFocusMgr.setMouseCapture( this ); + + if (mTakeFocusOnClick) + { + setFocus( TRUE ); + } + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::onFocusReceived() +{ + if (mMediaSource) + { + mMediaSource->focus(true); + + // Set focus for edit menu items + LLEditMenuHandler::gEditMenuHandler = mMediaSource; + } + + LLPanel::onFocusReceived(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::onFocusLost() +{ + if (mMediaSource) + { + mMediaSource->focus(false); + + if( LLEditMenuHandler::gEditMenuHandler == mMediaSource ) + { + // Clear focus for edit menu items + LLEditMenuHandler::gEditMenuHandler = NULL; + } + } + + gViewerWindow->focusClient(); + + LLPanel::onFocusLost(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::postBuild () +{ + mVisibleSignal.connect(boost::bind(&LLMediaCtrl::onVisibilityChange, this, _2)); + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::handleKeyHere( KEY key, MASK mask ) +{ + BOOL result = FALSE; + + // FIXME: THIS IS SO WRONG. + // Menu keys should be handled by the menu system and not passed to UI elements, but this is how LLTextEditor and LLLineEditor do it... + + if (mMediaSource) + { + if( MASK_CONTROL & mask ) + { + if( 'C' == key ) + { + mMediaSource->copy(); + result = TRUE; + } + else + if( 'V' == key ) + { + mMediaSource->paste(); + result = TRUE; + } + else + if( 'X' == key ) + { + mMediaSource->cut(); + result = TRUE; + } + } + + if(!result) + { + result = mMediaSource->handleKeyHere(key, mask); + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::handleVisibilityChange ( BOOL new_visibility ) +{ + llinfos << "visibility changed to " << (new_visibility?"true":"false") << llendl; + if(mMediaSource) + { + mMediaSource->setVisible( new_visibility ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +BOOL LLMediaCtrl::handleUnicodeCharHere(llwchar uni_char) +{ + BOOL result = FALSE; + + // only accept 'printable' characters, sigh... + if (uni_char >= 32 // discard 'control' characters + && uni_char != 127) // SDL thinks this is 'delete' - yuck. + { + if (mMediaSource) + result = mMediaSource->handleUnicodeCharHere(uni_char); + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::onVisibilityChange ( const LLSD& new_visibility ) +{ + // set state of frequent updates automatically if visibility changes + if ( new_visibility.asBoolean() ) + { + mFrequentUpdates = true; + } + else + { + mFrequentUpdates = false; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::reshape( S32 width, S32 height, BOOL called_from_parent ) +{ + S32 screen_width = mIgnoreUIScale ? llround((F32)width * LLUI::sGLScaleFactor.mV[VX]) : width; + S32 screen_height = mIgnoreUIScale ? llround((F32)height * LLUI::sGLScaleFactor.mV[VY]) : height; + +// llinfos << "reshape called with width = " << width << ", height = " << height << llendl; + + // when floater is minimized, these sizes are negative + if ( screen_height > 0 && screen_width > 0 ) + { + mMediaSource->setSize(screen_width, screen_height); + mForceUpdate = true; + } + + LLPanel::reshape( width, height, called_from_parent ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::navigateBack() +{ + if (mMediaSource && mMediaSource->hasMedia()) + { + mMediaSource->getMediaPlugin()->browse_back(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::navigateForward() +{ + if (mMediaSource && mMediaSource->hasMedia()) + { + mMediaSource->getMediaPlugin()->browse_forward(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMediaCtrl::canNavigateBack() +{ + if (mMediaSource) + return mMediaSource->canNavigateBack(); + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMediaCtrl::canNavigateForward() +{ + if (mMediaSource) + return mMediaSource->canNavigateForward(); + else + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::set404RedirectUrl( std::string redirect_url ) +{ + if(mMediaSource && mMediaSource->hasMedia()) + mMediaSource->getMediaPlugin()->set_status_redirect( 404, redirect_url ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::clr404RedirectUrl() +{ + if(mMediaSource && mMediaSource->hasMedia()) + mMediaSource->getMediaPlugin()->set_status_redirect(404, ""); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::navigateTo( std::string url_in, std::string mime_type) +{ + // don't browse to anything that starts with secondlife:// or sl:// + const std::string protocol1 = "secondlife://"; + const std::string protocol2 = "sl://"; + if ((LLStringUtil::compareInsensitive(url_in.substr(0, protocol1.length()), protocol1) == 0) || + (LLStringUtil::compareInsensitive(url_in.substr(0, protocol2.length()), protocol2) == 0)) + { + // TODO: Print out/log this attempt? + // llinfos << "Rejecting attempt to load restricted website :" << urlIn << llendl; + return; + } + + if (mMediaSource) + { + mCurrentNavUrl = url_in; + mMediaSource->navigateTo(url_in, mime_type, mime_type.empty()); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::navigateToLocalPage( const std::string& subdir, const std::string& filename_in ) +{ + std::string language = LLUI::getLanguage(); + std::string delim = gDirUtilp->getDirDelimiter(); + std::string filename; + + filename += subdir; + filename += delim; + filename += filename_in; + + std::string expanded_filename = gDirUtilp->findSkinnedFilename("html", language, filename); + + if (! gDirUtilp->fileExists(expanded_filename)) + { + if (language != "en-us") + { + expanded_filename = gDirUtilp->findSkinnedFilename("html", "en-us", filename); + if (! gDirUtilp->fileExists(expanded_filename)) + { + llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; + return; + } + } + else + { + llwarns << "File " << subdir << delim << filename_in << "not found" << llendl; + return; + } + } + if (mMediaSource) + { + mCurrentNavUrl = expanded_filename; + mMediaSource->navigateTo(expanded_filename, "text/html", false); + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::navigateHome() +{ + if( mHomePageUrl.length() ) + { + if (mMediaSource) + mMediaSource->navigateTo(mHomePageUrl); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::setHomePageUrl( const std::string urlIn ) +{ + mHomePageUrl = urlIn; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMediaCtrl::setCaretColor(unsigned int red, unsigned int green, unsigned int blue) +{ + //NOOP + return false; +} +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMediaCtrl::getHomePageUrl() +{ + return mHomePageUrl; +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLPluginClassMedia* LLMediaCtrl::getMediaPlugin() +{ + return mMediaSource.isNull() ? NULL : mMediaSource->getMediaPlugin(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::draw() +{ + LLPluginClassMedia* media_plugin = NULL; + + if(mMediaSource && mMediaSource->hasMedia()) + { + media_plugin = mMediaSource->getMediaPlugin(); + } + else + { + return; + } + + LLViewerMediaTexture* media_texture = LLViewerTextureManager::findMediaTexture(mMediaTextureID); + + if (!media_texture ) + { + return; + } + + if ( gRestoreGL == 1 ) + { + LLRect r = getRect(); + reshape( r.getWidth(), r.getHeight(), FALSE ); + return; + }; + + // NOTE: optimization needed here - probably only need to do this once + // unless tearoffs change the parent which they probably do. + const LLUICtrl* ptr = findRootMostFocusRoot(); + if ( ptr && ptr->hasFocus() ) + { + setFrequentUpdates( true ); + } + else + { + setFrequentUpdates( false ); + }; + + // alpha off for this + LLGLSUIDefault gls_ui; + LLGLDisable gls_alphaTest( GL_ALPHA_TEST ); + + gGL.pushMatrix(); + { + if (mIgnoreUIScale) + { + glLoadIdentity(); + // font system stores true screen origin, need to scale this by UI scale factor + // to get render origin for this view (with unit scale) + gGL.translatef(floorf(LLFontGL::sCurOrigin.mX * LLUI::sGLScaleFactor.mV[VX]), + floorf(LLFontGL::sCurOrigin.mY * LLUI::sGLScaleFactor.mV[VY]), + LLFontGL::sCurOrigin.mZ); + } + + // scale texture to fit the space using texture coords + gGL.getTexUnit(0)->bind(media_texture); + gGL.color4fv( LLColor4::white.mV ); + F32 max_u = ( F32 )media_plugin->getWidth() / ( F32 )media_plugin->getTextureWidth(); + F32 max_v = ( F32 )media_plugin->getHeight() / ( F32 )media_plugin->getTextureHeight(); + + LLRect r = getRect(); + S32 width, height; + S32 x_offset = 0; + S32 y_offset = 0; + + if(mStretchToFill) + { + if(mMaintainAspectRatio) + { + F32 media_aspect = (F32)(media_plugin->getWidth()) / (F32)(media_plugin->getHeight()); + F32 view_aspect = (F32)(r.getWidth()) / (F32)(r.getHeight()); + if(media_aspect > view_aspect) + { + // max width, adjusted height + width = r.getWidth(); + height = llmin(llmax(S32(width / media_aspect), 0), r.getHeight()); + } + else + { + // max height, adjusted width + height = r.getHeight(); + width = llmin(llmax(S32(height * media_aspect), 0), r.getWidth()); + } + } + else + { + width = r.getWidth(); + height = r.getHeight(); + } + } + else + { + width = llmin(media_plugin->getWidth(), r.getWidth()); + height = llmin(media_plugin->getHeight(), r.getHeight()); + } + + x_offset = (r.getWidth() - width) / 2; + y_offset = (r.getHeight() - height) / 2; + + // draw the browser + gGL.setSceneBlendType(LLRender::BT_REPLACE); + gGL.begin( LLRender::QUADS ); + if (! media_plugin->getTextureCoordsOpenGL()) + { + // render using web browser reported width and height, instead of trying to invert GL scale + gGL.texCoord2f( max_u, 0.f ); + gGL.vertex2i( x_offset + width, y_offset + height ); + + gGL.texCoord2f( 0.f, 0.f ); + gGL.vertex2i( x_offset, y_offset + height ); + + gGL.texCoord2f( 0.f, max_v ); + gGL.vertex2i( x_offset, y_offset ); + + gGL.texCoord2f( max_u, max_v ); + gGL.vertex2i( x_offset + width, y_offset ); + } + else + { + // render using web browser reported width and height, instead of trying to invert GL scale + gGL.texCoord2f( max_u, max_v ); + gGL.vertex2i( x_offset + width, y_offset + height ); + + gGL.texCoord2f( 0.f, max_v ); + gGL.vertex2i( x_offset, y_offset + height ); + + gGL.texCoord2f( 0.f, 0.f ); + gGL.vertex2i( x_offset, y_offset ); + + gGL.texCoord2f( max_u, 0.f ); + gGL.vertex2i( x_offset + width, y_offset ); + } + gGL.end(); + gGL.setSceneBlendType(LLRender::BT_ALPHA); + } + gGL.popMatrix(); + + // highlight if keyboard focus here. (TODO: this needs some work) + if ( mBorder && mBorder->getVisible() ) + mBorder->setKeyboardFocusHighlight( gFocusMgr.childHasKeyboardFocus( this ) ); + + + LLPanel::draw(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::convertInputCoords(S32& x, S32& y) +{ + bool coords_opengl = false; + + if(mMediaSource && mMediaSource->hasMedia()) + { + coords_opengl = mMediaSource->getMediaPlugin()->getTextureCoordsOpenGL(); + } + + x = mIgnoreUIScale ? llround((F32)x * LLUI::sGLScaleFactor.mV[VX]) : x; + if ( ! coords_opengl ) + { + y = mIgnoreUIScale ? llround((F32)(y) * LLUI::sGLScaleFactor.mV[VY]) : y; + } + else + { + y = mIgnoreUIScale ? llround((F32)(getRect().getHeight() - y) * LLUI::sGLScaleFactor.mV[VY]) : getRect().getHeight() - y; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// static +bool LLMediaCtrl::onClickLinkExternalTarget(const LLSD& notification, const LLSD& response ) +{ + S32 option = LLNotification::getSelectedOption(notification, response); + if ( 0 == option ) + { + // open in external browser because we don't support + // creation of our own secondary browser windows + LLWeb::loadURLExternal( notification["payload"]["external_url"].asString() ); + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// inherited from LLViewerMediaObserver +//virtual +void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + switch(event) + { + case MEDIA_EVENT_CONTENT_UPDATED: + { + // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << LL_ENDL; + }; + break; + + case MEDIA_EVENT_TIME_DURATION_UPDATED: + { + // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_SIZE_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_SIZE_CHANGED " << LL_ENDL; + LLRect r = getRect(); + reshape( r.getWidth(), r.getHeight(), FALSE ); + }; + break; + + case MEDIA_EVENT_CURSOR_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL; + + std::string cursor = self->getCursorName(); + + if(cursor == "arrow") + mLastSetCursor = UI_CURSOR_ARROW; + else if(cursor == "ibeam") + mLastSetCursor = UI_CURSOR_IBEAM; + else if(cursor == "splith") + mLastSetCursor = UI_CURSOR_SIZEWE; + else if(cursor == "splitv") + mLastSetCursor = UI_CURSOR_SIZENS; + else if(cursor == "hand") + mLastSetCursor = UI_CURSOR_HAND; + else // for anything else, default to the arrow + mLastSetCursor = UI_CURSOR_ARROW; + }; + break; + + case MEDIA_EVENT_NAVIGATE_BEGIN: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN, url is " << self->getNavigateURI() << LL_ENDL; + if(mMediaSource && mHideLoading) + { + mMediaSource->suspendUpdates(true); + } + }; + break; + + case MEDIA_EVENT_NAVIGATE_COMPLETE: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << LL_ENDL; + if(mMediaSource && mHideLoading) + { + mMediaSource->suspendUpdates(false); + } + }; + break; + + case MEDIA_EVENT_PROGRESS_UPDATED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << LL_ENDL; + }; + break; + + case MEDIA_EVENT_STATUS_TEXT_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_LOCATION_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_CLICK_LINK_HREF: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL; + onClickLinkHref(self); + }; + break; + + case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << LL_ENDL; + onClickLinkNoFollow(self); + }; + break; + + case MEDIA_EVENT_PLUGIN_FAILED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << LL_ENDL; + }; + break; + }; + + // chain all events to any potential observers of this object. + emitEvent(self, event); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::onClickLinkHref( LLPluginClassMedia* self ) +{ + // retrieve the event parameters + std::string target = self->getClickTarget(); + std::string url = self->getClickURL(); + + // if there is a value for the target + if ( !target.empty() ) + { + if ( target == "_external" ) + { + mExternalUrl = url; + LLSD payload; + payload["external_url"] = mExternalUrl; + LLNotifications::instance().add( "WebLaunchExternalTarget", LLSD(), payload, onClickLinkExternalTarget); + return; + } + } + + const std::string protocol1( "http://" ); + const std::string protocol2( "https://" ); + if( mOpenLinksInExternalBrowser ) + { + if ( !url.empty() ) + { + if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 || + LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 ) + { + LLWeb::loadURLExternal( url ); + } + } + } + else + if( mOpenLinksInInternalBrowser ) + { + if ( !url.empty() ) + { + if ( LLStringUtil::compareInsensitive( url.substr( 0, protocol1.length() ), protocol1 ) == 0 || + LLStringUtil::compareInsensitive( url.substr( 0, protocol2.length() ), protocol2 ) == 0 ) + { + // If we spawn a new LLFloaterHTML, assume we want it to + // follow this LLMediaCtrl's trust for whether or + // not to open secondlife:///app/ links. JC. +// const bool open_links_externally = false; +// LLFloaterHtml::getInstance()->show( +// event_in.mStringPayload, +// "Second Life Browser", +// open_links_externally, +// mTrusted); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaCtrl::onClickLinkNoFollow( LLPluginClassMedia* self ) +{ + std::string url = self->getClickURL(); + if (LLSLURL::isSLURLCommand(url) + && !mTrusted) + { + // block handling of this secondlife:///app/ URL + LLNotifications::instance().add("UnableToOpenCommandURL"); + return; + } + + LLURLDispatcher::dispatch(url, this, mTrusted); +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMediaCtrl::getCurrentNavUrl() +{ + return mCurrentNavUrl; +} + diff --git a/indra/newview/llmediactrl.h b/indra/newview/llmediactrl.h new file mode 100644 index 0000000000..a19b3ad67b --- /dev/null +++ b/indra/newview/llmediactrl.h @@ -0,0 +1,182 @@ +/** + * @file llmediactrl.h + * @brief Web browser UI control + * + * $LicenseInfo:firstyear=2006&license=viewergpl$ + * + * Copyright (c) 2006-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_LLMediaCtrl_H +#define LL_LLMediaCtrl_H + +#include "llviewermedia.h" + +#include "lluictrl.h" +#include "llframetimer.h" +#include "lldynamictexture.h" + +class LLViewBorder; +class LLUICtrlFactory; + +//////////////////////////////////////////////////////////////////////////////// +// +class LLMediaCtrl : + public LLPanel, + public LLViewerMediaObserver, + public LLViewerMediaEventEmitter +{ + LOG_CLASS(LLMediaCtrl); + +public: + struct Params : public LLInitParam::Block<Params, LLPanel::Params> + { + Optional<std::string> start_url; + + Optional<bool> border_visible, + ignore_ui_scale, + hide_loading; + + Optional<LLUIColor> caret_color; + + Params(); + }; + +protected: + LLMediaCtrl(const Params&); + friend class LLUICtrlFactory; + +public: + virtual ~LLMediaCtrl(); + + void setBorderVisible( BOOL border_visible ); + + // For the tutorial window, we don't want to take focus on clicks, + // as the examples include how to move around with the arrow + // keys. Thus we keep focus on the app by setting this false. + // Defaults to true. + void setTakeFocusOnClick( bool take_focus ); + + // handle mouse related methods + virtual BOOL handleHover( S32 x, S32 y, MASK mask ); + virtual BOOL handleMouseUp( S32 x, S32 y, MASK mask ); + virtual BOOL handleMouseDown( S32 x, S32 y, MASK mask ); + virtual BOOL handleDoubleClick( S32 x, S32 y, MASK mask ); + virtual BOOL handleScrollWheel( S32 x, S32 y, S32 clicks ); + + // navigation + void navigateTo( std::string url_in, std::string mime_type = ""); + void navigateBack(); + void navigateHome(); + void navigateForward(); + void navigateToLocalPage( const std::string& subdir, const std::string& filename_in ); + bool canNavigateBack(); + bool canNavigateForward(); + void setOpenInExternalBrowser( bool valIn ); + void setOpenInInternalBrowser( bool valIn ); + std::string getCurrentNavUrl(); + + // By default, we do not handle "secondlife:///app/" SLURLs, because + // those can cause teleports, open windows, etc. We cannot be sure + // that each "click" is actually due to a user action, versus + // Javascript or some other mechanism. However, we need the search + // floater and login page to handle these URLs. Those are safe + // because we control the page content. See DEV-9530. JC. + void setTrusted( bool valIn ); + + void setHomePageUrl( const std::string urlIn ); + std::string getHomePageUrl(); + + // set/clear URL to visit when a 404 page is reached + void set404RedirectUrl( std::string redirect_url ); + void clr404RedirectUrl(); + + // accessor/mutator for flag that indicates if frequent updates to texture happen + bool getFrequentUpdates() { return mFrequentUpdates; }; + void setFrequentUpdates( bool frequentUpdatesIn ) { mFrequentUpdates = frequentUpdatesIn; }; + + void setIgnoreUIScale(bool ignore) { mIgnoreUIScale = ignore; } + bool getIgnoreUIScale() { return mIgnoreUIScale; } + + void setAlwaysRefresh(bool refresh) { mAlwaysRefresh = refresh; } + bool getAlwaysRefresh() { return mAlwaysRefresh; } + + void setForceUpdate(bool force_update) { mForceUpdate = force_update; } + bool getForceUpdate() { return mForceUpdate; } + + LLPluginClassMedia* getMediaPlugin(); + + bool setCaretColor( unsigned int red, unsigned int green, unsigned int blue ); + + + // over-rides + virtual BOOL handleKeyHere( KEY key, MASK mask); + virtual void handleVisibilityChange ( BOOL new_visibility ); + virtual BOOL handleUnicodeCharHere(llwchar uni_char); + virtual void reshape( S32 width, S32 height, BOOL called_from_parent = TRUE); + virtual void draw(); + virtual BOOL postBuild(); + + // focus overrides + void onFocusLost(); + void onFocusReceived(); + + // Incoming media event dispatcher + virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + + // handlers for individual events (could be done inside the switch in handleMediaEvent, they're just individual functions for clarity) + void onClickLinkHref( LLPluginClassMedia* self ); + void onClickLinkNoFollow( LLPluginClassMedia* self ); + + protected: + void convertInputCoords(S32& x, S32& y); + + private: + void onVisibilityChange ( const LLSD& new_visibility ); + static bool onClickLinkExternalTarget( const LLSD&, const LLSD& ); + + const S32 mTextureDepthBytes; + LLUUID mMediaTextureID; + LLViewBorder* mBorder; + bool mFrequentUpdates; + bool mForceUpdate; + bool mOpenLinksInExternalBrowser; + bool mOpenLinksInInternalBrowser; + bool mTrusted; + std::string mHomePageUrl; + std::string mExternalUrl; + std::string mCurrentNavUrl; + bool mIgnoreUIScale; + bool mAlwaysRefresh; + viewer_media_t mMediaSource; + bool mTakeFocusOnClick; + ECursorType mLastSetCursor; + bool mStretchToFill; + bool mMaintainAspectRatio; + bool mHideLoading; +}; + +#endif // LL_LLMediaCtrl_H diff --git a/indra/newview/llmimetypes.cpp b/indra/newview/llmimetypes.cpp index bfbc81aa66..525e89cdff 100644 --- a/indra/newview/llmimetypes.cpp +++ b/indra/newview/llmimetypes.cpp @@ -46,6 +46,8 @@ std::string sDefaultWidgetType; // Returned when we don't know what widget set to use std::string sDefaultImpl; // Returned when we don't know what impl to use +std::string sXMLFilename; + // Squirrel away XML filename so we know how to reset ///////////////////////////////////////////////////////////////////////////// @@ -146,6 +148,8 @@ bool LLMIMETypes::parseMIMETypes(const std::string& xml_filename) sWidgetMap[set_name] = info; } } + + sXMLFilename = xml_filename; return true; } @@ -267,3 +271,23 @@ bool LLMIMETypes::findAllowLooping(const std::string& mime_type) } return allow_looping; } + +// static +bool LLMIMETypes::isTypeHandled(const std::string& mime_type) +{ + mime_info_map_t::const_iterator it = sMap.find(mime_type); + if (it != sMap.end()) + { + return true; + } + return false; +} + +// static +void LLMIMETypes::reload(void*) +{ + sMap.clear(); + sWidgetMap.clear(); + (void)LLMIMETypes::parseMIMETypes(sXMLFilename); +} + diff --git a/indra/newview/llmimetypes.h b/indra/newview/llmimetypes.h index 7a50c29429..b217ce7a81 100644 --- a/indra/newview/llmimetypes.h +++ b/indra/newview/llmimetypes.h @@ -72,6 +72,12 @@ public: static bool findAllowLooping(const std::string& mime_type); // accessor for flag to enable/disable media looping checkbox + static bool isTypeHandled(const std::string& mime_type); + // determines if the specific mime type is handled by the media system + + static void reload(void*); + // re-loads the MIME types file from the file path last passed into parseMIMETypes + public: struct LLMIMEInfo { diff --git a/indra/newview/llmoveview.cpp b/indra/newview/llmoveview.cpp index fc425d1b33..11c8b03f7f 100644 --- a/indra/newview/llmoveview.cpp +++ b/indra/newview/llmoveview.cpp @@ -36,6 +36,7 @@ // Library includes #include "indra_constants.h" +#include "llparcel.h" // Viewer includes @@ -51,6 +52,8 @@ #include "llviewerwindow.h" #include "llviewercontrol.h" #include "llselectmgr.h" +#include "llviewerparcelmgr.h" +#include "llviewerregion.h" // // Constants @@ -137,6 +140,8 @@ BOOL LLFloaterMove::postBuild() initMovementMode(); + LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback(LLFloaterMove::sUpdateFlyingStatus); + return TRUE; } @@ -385,6 +390,15 @@ void LLFloaterMove::updatePosition() } setOrigin(x, y); } + +//static +void LLFloaterMove::sUpdateFlyingStatus() +{ + LLFloaterMove *floater = LLFloaterReg::findTypedInstance<LLFloaterMove>("moveview"); + if (floater) floater->mModeControlButtonMap[MM_FLY]->setEnabled(gAgent.canFly()); + +} + void LLFloaterMove::showModeButtons(BOOL bShow) { if (mModeActionsPanel->getVisible() == bShow) @@ -421,6 +435,8 @@ void LLFloaterMove::enableInstance(BOOL bEnable) void LLFloaterMove::onOpen(const LLSD& key) { updatePosition(); + + sUpdateFlyingStatus(); } void LLFloaterMove::showQuickTips(const EMovementMode mode) @@ -476,11 +492,12 @@ inline LLPanelStandStopFlying* LLPanelStandStopFlying::getInstance() void LLPanelStandStopFlying::setStandStopFlyingMode(EStandStopFlyingMode mode) { LLPanelStandStopFlying* panel = getInstance(); - panel->setVisible(TRUE); - BOOL standVisible = SSFM_STAND == mode; - panel->mStandButton->setVisible(standVisible); - panel->mStopFlyingButton->setVisible(!standVisible); + panel->mStandButton->setVisible(SSFM_STAND == mode); + panel->mStopFlyingButton->setVisible(SSFM_STOP_FLYING == mode); + + //visibility of it should be updated after updating visibility of the buttons + panel->setVisible(TRUE); } //static @@ -505,11 +522,12 @@ BOOL LLPanelStandStopFlying::postBuild() mStandButton = getChild<LLButton>("stand_btn"); mStandButton->setCommitCallback(boost::bind(&LLPanelStandStopFlying::onStandButtonClick, this)); mStandButton->setCommitCallback(boost::bind(&LLFloaterMove::enableInstance, TRUE)); + mStandButton->setVisible(FALSE); mStopFlyingButton = getChild<LLButton>("stop_fly_btn"); mStopFlyingButton->setCommitCallback(boost::bind(&LLFloaterMove::setFlyingMode, FALSE)); mStopFlyingButton->setCommitCallback(boost::bind(&LLPanelStandStopFlying::onStopFlyingButtonClick, this)); - + mStopFlyingButton->setVisible(FALSE); return TRUE; } @@ -517,6 +535,11 @@ BOOL LLPanelStandStopFlying::postBuild() //virtual void LLPanelStandStopFlying::setVisible(BOOL visible) { + //we dont need to show the panel if these buttons are not activated + if (visible && !mStandButton->getVisible() && !mStopFlyingButton->getVisible()) visible = false; + + if (gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK) visible = false; + if (visible) { updatePosition(); @@ -549,6 +572,7 @@ void LLPanelStandStopFlying::onStandButtonClick() LLSelectMgr::getInstance()->deselectAllForStandingUp(); gAgent.setControlFlags(AGENT_CONTROL_STAND_UP); + setFocus(FALSE); // EXT-482 setVisible(FALSE); } @@ -556,6 +580,7 @@ void LLPanelStandStopFlying::onStopFlyingButtonClick() { gAgent.setFlying(FALSE); + setFocus(FALSE); // EXT-482 setVisible(FALSE); } diff --git a/indra/newview/llmoveview.h b/indra/newview/llmoveview.h index fd9cf9f4c1..6e6af9b693 100644 --- a/indra/newview/llmoveview.h +++ b/indra/newview/llmoveview.h @@ -67,6 +67,7 @@ public: // let update its position in each frame /*virtual*/ void draw(){updatePosition(); LLFloater::draw();} + static void sUpdateFlyingStatus(); protected: void turnLeft(); diff --git a/indra/newview/llnamebox.cpp b/indra/newview/llnamebox.cpp index 56648d3218..2f4a266198 100644 --- a/indra/newview/llnamebox.cpp +++ b/indra/newview/llnamebox.cpp @@ -41,7 +41,6 @@ #include "lluuid.h" #include "llcachename.h" -#include "llagent.h" // statics std::set<LLNameBox*> LLNameBox::sInstances; diff --git a/indra/newview/llnameeditor.cpp b/indra/newview/llnameeditor.cpp index ccb33c770a..65601da7da 100644 --- a/indra/newview/llnameeditor.cpp +++ b/indra/newview/llnameeditor.cpp @@ -34,7 +34,6 @@ #include "llnameeditor.h" #include "llcachename.h" -#include "llagent.h" #include "llfontgl.h" diff --git a/indra/newview/llnamelistctrl.cpp b/indra/newview/llnamelistctrl.cpp index 087fdda14a..1b82c2dc18 100644 --- a/indra/newview/llnamelistctrl.cpp +++ b/indra/newview/llnamelistctrl.cpp @@ -37,7 +37,6 @@ #include "llnamelistctrl.h" #include "llcachename.h" -#include "llagent.h" #include "llinventory.h" #include "llscrolllistitem.h" #include "llscrolllistcell.h" diff --git a/indra/newview/llnavigationbar.cpp b/indra/newview/llnavigationbar.cpp index 0f719f29e9..46bbb382a5 100644 --- a/indra/newview/llnavigationbar.cpp +++ b/indra/newview/llnavigationbar.cpp @@ -38,10 +38,9 @@ #include <llfocusmgr.h> #include <lliconctrl.h> #include <llmenugl.h> -#include <llwindow.h> #include "llagent.h" -#include "llfloaterhtmlhelp.h" +#include "llviewerregion.h" #include "lllandmarkactions.h" #include "lllocationhistory.h" #include "lllocationinputctrl.h" @@ -51,12 +50,17 @@ #include "llslurl.h" #include "llurlsimstring.h" #include "llviewerinventory.h" -#include "llviewermenu.h" #include "llviewerparcelmgr.h" #include "llworldmap.h" #include "llappviewer.h" #include "llviewercontrol.h" +#include "llfloatermediabrowser.h" + +#include "llinventorymodel.h" +#include "lllandmarkactions.h" + #include "llfavoritesbar.h" +#include "llagentui.h" //-- LLTeleportHistoryMenuItem ----------------------------------------------- @@ -103,7 +107,7 @@ const std::string LLTeleportHistoryMenuItem::ICON_IMG_FORWARD("teleport_history_ LLTeleportHistoryMenuItem::Params::Params(EType type, std::string title) { item_type(type); - font.name("SansSerif"); + font.name("SANSSERIF"); if (type == TYPE_CURRENT) font.style("BOLD"); @@ -123,7 +127,6 @@ LLTeleportHistoryMenuItem::LLTeleportHistoryMenuItem(const Params& p) icon_params.rect(LLRect(0, ICON_HEIGHT, ICON_WIDTH, 0)); icon_params.mouse_opaque(false); icon_params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); - icon_params.tab_stop(false); icon_params.visible(false); mArrowIcon = LLUICtrlFactory::create<LLIconCtrl> (icon_params); @@ -176,29 +179,20 @@ LLNavigationBar* LLNavigationBar::getInstance() LLNavigationBar::LLNavigationBar() : mTeleportHistoryMenu(NULL), - mLocationContextMenu(NULL), mBtnBack(NULL), mBtnForward(NULL), mBtnHome(NULL), mCmbLocation(NULL), mLeSearch(NULL), - mPurgeTPHistoryItems(false), - mUpdateTypedLocationHistory(false) + mPurgeTPHistoryItems(false) { setIsChrome(TRUE); - mParcelMgrConnection = LLViewerParcelMgr::getInstance()->setAgentParcelChangedCallback( - boost::bind(&LLNavigationBar::onTeleportFinished, this)); + mParcelMgrConnection = LLViewerParcelMgr::getInstance()->setTeleportFinishedCallback( + boost::bind(&LLNavigationBar::onTeleportFinished, this, _1)); - // Register callbacks and load the location field context menu (NB: the order matters). - mCommitCallbackRegistrar.add("Navbar.Action", boost::bind(&LLNavigationBar::onLocationContextMenuItemClicked, this, _2)); - mEnableCallbackRegistrar.add("Navbar.EnableMenuItem", boost::bind(&LLNavigationBar::onLocationContextMenuItemEnabled, this, _2)); - LLUICtrlFactory::getInstance()->buildPanel(this, "panel_navigation_bar.xml"); - // navigation bar can never get a tab - setFocusRoot(FALSE); - // set a listener function for LoginComplete event LLAppViewer::instance()->setOnLoginCompletedCallback(boost::bind(&LLNavigationBar::handleLoginComplete, this)); @@ -242,14 +236,6 @@ BOOL LLNavigationBar::postBuild() mLeSearch->setCommitCallback(boost::bind(&LLNavigationBar::onSearchCommit, this)); - // Load the location field context menu - mLocationContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); - if (!mLocationContextMenu) - { - llwarns << "Error loading navigation bar context menu" << llendl; - return FALSE; - } - mDefaultNbRect = getRect(); mDefaultFpRect = getChild<LLFavoritesBarCtrl>("favorite")->getRect(); @@ -271,32 +257,6 @@ void LLNavigationBar::draw() LLPanel::draw(); } -BOOL LLNavigationBar::handleRightMouseUp(S32 x, S32 y, MASK mask) -{ - // *HACK. We should use mCmbLocation's right click callback instead. - - // If the location field is clicked then show its context menu. - if (mCmbLocation->getRect().pointInRect(x, y)) - { - // Pass the focus to the line editor when it is right-clicked - mCmbLocation->setFocus(TRUE); - - if (mLocationContextMenu) - { - mLocationContextMenu->buildDrawLabels(); - mLocationContextMenu->updateParent(LLMenuGL::sMenuContainer); - LLLineEditor* textEntry =mCmbLocation->getTextEntry(); - if(textEntry && !textEntry->hasSelection() ){ - textEntry->setText(gAgent.getUnescapedSLURL()); - textEntry->selectAll(); - } - LLMenuGL::showPopup(this, mLocationContextMenu, x, y); - } - return TRUE; - } - return LLPanel:: handleRightMouseUp(x, y, mask); -} - void LLNavigationBar::onBackButtonClicked() { LLTeleportHistory::getInstance()->goBack(); @@ -354,11 +314,18 @@ void LLNavigationBar::onLocationSelection() } else { - region_name = extractLocalCoordsFromRegName(typed_location, &x, &y, &z); - - if (region_name != typed_location) { - local_coords.set(x, y, z); + //If it is not slurl let's look for landmarks + LLInventoryModel::item_array_t landmark_items = LLLandmarkActions::fetchLandmarksByName(typed_location, FALSE); + if ( !landmark_items.empty() ) + { + gAgent.teleportViaLandmark(landmark_items[0]->getAssetUUID()); + return; } + //No landmark match, check if it is a region name + region_name = parseLocation(typed_location, &x, &y, &z); + if (region_name != typed_location) + local_coords.set(x, y, z); + // Treat it as region name. // region_name = typed_location; } @@ -371,30 +338,30 @@ void LLNavigationBar::onLocationSelection() LLWorldMap::getInstance()->sendNamedRegionRequest(region_name, cb, std::string("unused"), false); } -void LLNavigationBar::onTeleportFinished() { - - if (mUpdateTypedLocationHistory) { - LLLocationHistory* lh = LLLocationHistory::getInstance(); - - // Location is valid. Add it to the typed locations history. - // If user has typed text this variable will contain -1. - if (mCmbLocation->getCurrentIndex() != -1) { - lh->touchItem(mCmbLocation->getSelectedItemLabel()); - } else { - std::string region_name; - std::string url = gAgent.getSLURL(); - S32 x = 0, y = 0, z = 0; - - if (LLSLURL::isSLURL(url)) { - LLURLSimString::parse(LLSLURL::stripProtocol(url), ®ion_name, &x, &y, &z); - appendLocalCoordsToRegName(®ion_name, x, y, z); - lh->addItem(region_name, url); - } - } +void LLNavigationBar::onTeleportFinished(const LLVector3d& global_agent_pos) +{ + // Location is valid. Add it to the typed locations history. + LLLocationHistory* lh = LLLocationHistory::getInstance(); - lh->save(); - mUpdateTypedLocationHistory = false; + std::string location; + /*NOTE: + * We can't use gAgent.getPositionAgent() in case of local teleport to build location. + * At this moment gAgent.getPositionAgent() contains previous coordinates. + * according to EXT-65 agent position is being reseted on each frame. + */ + LLAgentUI::buildLocationString(location, LLAgent::LOCATION_FORMAT_WITHOUT_SIM, + gAgent.getPosAgentFromGlobal(global_agent_pos)); + + //Touch it, if it is at list already, add new location otherwise + if ( !lh->touchItem(location) ) { + std::string tooltip = LLSLURL::buildSLURLfromPosGlobal( + gAgent.getRegion()->getName(), global_agent_pos, false); + + lh->addItem(location, tooltip); } + llinfos << "Saving after on teleport finish" << llendl; + lh->save(); + } void LLNavigationBar::onTeleportHistoryChanged() @@ -467,7 +434,6 @@ void LLNavigationBar::onRegionNameResponse( LLVector3d region_pos = from_region_handle(region_handle); LLVector3d global_pos = region_pos + (LLVector3d) local_coords; - mUpdateTypedLocationHistory = true; llinfos << "Teleporting to: " << global_pos << llendl; gAgent.teleportViaLocation(global_pos); } @@ -496,77 +462,6 @@ void LLNavigationBar::showTeleportHistoryMenu() gFocusMgr.setMouseCapture( NULL ); } -void LLNavigationBar::onLocationContextMenuItemClicked(const LLSD& userdata) -{ - std::string item = userdata.asString(); - LLLineEditor* location_entry = mCmbLocation->getTextEntry(); - - if (item == std::string("show_coordinates")) - { - gSavedSettings.setBOOL("ShowCoordinatesOption",!gSavedSettings.getBOOL("ShowCoordinatesOption")); - } - else if (item == std::string("landmark")) - { - LLSideTray::getInstance()->showPanel("panel_places", LLSD().insert("type", "create_landmark")); - } - else if (item == std::string("cut")) - { - location_entry->cut(); - } - else if (item == std::string("copy")) - { - location_entry->copy(); - } - else if (item == std::string("paste")) - { - location_entry->paste(); - } - else if (item == std::string("delete")) - { - location_entry->deleteSelection(); - } - else if (item == std::string("select_all")) - { - location_entry->selectAll(); - } -} - -bool LLNavigationBar::onLocationContextMenuItemEnabled(const LLSD& userdata) -{ - std::string item = userdata.asString(); - const LLLineEditor* location_entry = mCmbLocation->getTextEntry(); - - if (item == std::string("can_cut")) - { - return location_entry->canCut(); - } - else if (item == std::string("can_copy")) - { - return location_entry->canCopy(); - } - else if (item == std::string("can_paste")) - { - return location_entry->canPaste(); - } - else if (item == std::string("can_delete")) - { - return location_entry->canDeselect(); - } - else if (item == std::string("can_select_all")) - { - return location_entry->canSelectAll(); - } - else if(item == std::string("can_landmark")) - { - return !LLLandmarkActions::landmarkAlreadyExists(); - }else if(item == std::string("show_coordinates")){ - - return gSavedSettings.getBOOL("ShowCoordinatesOption"); - } - - return false; -} - void LLNavigationBar::handleLoginComplete() { mCmbLocation->handleLoginComplete(); @@ -577,12 +472,7 @@ void LLNavigationBar::invokeSearch(std::string search_text) LLFloaterReg::showInstance("search", LLSD().insert("panel", "all").insert("id", LLSD(search_text))); } -void LLNavigationBar::appendLocalCoordsToRegName(std::string* reg_name, S32 x, S32 y, S32 z) { - std::string fmt = *reg_name + " (%d, %d, %d)"; - *reg_name = llformat(fmt.c_str(), x, y, z); -} - -std::string LLNavigationBar::extractLocalCoordsFromRegName(const std::string & reg_name, S32* x, S32* y, S32* z) { +std::string LLNavigationBar::parseLocation(const std::string & location, S32* x, S32* y, S32* z) { /* * This regular expression extracts numbers from the following string * construct: "(num1, num2, num3)", where num1, num2 and num3 are decimal @@ -591,7 +481,7 @@ std::string LLNavigationBar::extractLocalCoordsFromRegName(const std::string & r const boost::regex re("\\s*\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)\\s*"); boost::smatch m; - if (boost::regex_search(reg_name, m, re)) { + if (boost::regex_search(location, m, re)) { // string representations of parsed by regex++ numbers std::string xstr(m[1].first, m[1].second); std::string ystr(m[2].first, m[2].second); @@ -600,13 +490,15 @@ std::string LLNavigationBar::extractLocalCoordsFromRegName(const std::string & r *x = atoi(xstr.c_str()); *y = atoi(ystr.c_str()); *z = atoi(zstr.c_str()); - - return boost::regex_replace(reg_name, re, ""); + //erase commas in coordinates + std::string region_parcel = boost::regex_replace(location, re, ""); + // cut region name + return region_parcel.substr(0, region_parcel.find_first_of(',')); } *x = *y = *z = 0; - return reg_name; + return location; } void LLNavigationBar::clearHistoryCache() diff --git a/indra/newview/llnavigationbar.h b/indra/newview/llnavigationbar.h index c533953a02..6932847854 100644 --- a/indra/newview/llnavigationbar.h +++ b/indra/newview/llnavigationbar.h @@ -56,7 +56,6 @@ public: /*virtual*/ void draw(); /*virtual*/ BOOL postBuild(); - /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); void handleLoginComplete(); void clearHistoryCache(); @@ -71,12 +70,12 @@ private: void showTeleportHistoryMenu(); void invokeSearch(std::string search_text); - static void appendLocalCoordsToRegName(std::string* reg_name, S32 x, S32 y, S32 z); - static std::string extractLocalCoordsFromRegName(const std::string & reg_name, S32* x, S32* y, S32* z); + /** + * Get region name and local coordinates from typed location + */ + static std::string parseLocation(const std::string & location, S32* x, S32* y, S32* z); // callbacks - bool onLocationContextMenuItemEnabled(const LLSD& userdata); - void onLocationContextMenuItemClicked(const LLSD& userdata); void onTeleportHistoryMenuItemClicked(const LLSD& userdata); void onTeleportHistoryChanged(); void onBackButtonClicked(); @@ -87,7 +86,7 @@ private: void onLocationSelection(); void onLocationPrearrange(const LLSD& data); void onSearchCommit(); - void onTeleportFinished(); + void onTeleportFinished(const LLVector3d& global_agent_pos); void onRegionNameResponse( std::string typed_location, std::string region_name, @@ -97,7 +96,6 @@ private: static LLNavigationBar *sInstance; - LLMenuGL* mLocationContextMenu; LLMenuGL* mTeleportHistoryMenu; LLButton* mBtnBack; LLButton* mBtnForward; @@ -108,7 +106,6 @@ private: LLRect mDefaultFpRect; boost::signals2::connection mParcelMgrConnection; bool mPurgeTPHistoryItems; - bool mUpdateTypedLocationHistory; }; #endif diff --git a/indra/newview/llnearbychat.cpp b/indra/newview/llnearbychat.cpp index c89715e815..3856a86da0 100644 --- a/indra/newview/llnearbychat.cpp +++ b/indra/newview/llnearbychat.cpp @@ -54,6 +54,8 @@ #include "llviewertexteditor.h" #include "llstylemap.h" +#include "lldraghandle.h" + static const S32 RESIZE_BAR_THICKNESS = 3; @@ -88,6 +90,8 @@ BOOL LLNearbyChat::postBuild() mResizeHandle[2]->setVisible(false); mResizeHandle[3]->setVisible(false); + getDragHandle()->setVisible(false); + //menu LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar; @@ -204,11 +208,20 @@ void nearbychat_add_timestamped_line(LLViewerTextEditor* edit, LLChat chat, cons { std::string start_line = line.substr(0, chat.mFromName.length() + 1); line = line.substr(chat.mFromName.length() + 1); - const LLStyleSP &sourceStyle = LLStyleMap::instance().lookup(chat.mFromID,chat.mURL); - edit->appendStyledText(start_line, false, prepend_newline, sourceStyle); + edit->appendStyledText(start_line, false, prepend_newline, LLStyleMap::instance().lookup(chat.mFromID,chat.mURL)); prepend_newline = false; } - edit->appendColoredText(line, false, prepend_newline, color); + + S32 font_size = gSavedSettings.getS32("ChatFontSize"); + + std::string font_name = ""; + + if (0 == font_size) + font_name = "small"; + else if (2 == font_size) + font_name = "sansserifbig"; + + edit->appendColoredText(line, false, prepend_newline, color, font_name); } @@ -339,6 +352,7 @@ BOOL LLNearbyChat::handleMouseDown (S32 x, S32 y, MASK mask) S32 local_y = caption_local_y - nearby_speakers_btn->getRect().mBottom; if(nearby_speakers_btn->pointInView(local_x, local_y)) { + onNearbySpeakers(); bringToFront( x, y ); return true; @@ -415,6 +429,8 @@ void LLNearbyChat::pinn_panel() mResizeBar[LLResizeBar::LEFT]->setVisible(false); mResizeBar[LLResizeBar::RIGHT]->setVisible(false); + getDragHandle()->setVisible(false); + } void LLNearbyChat::float_panel() @@ -427,6 +443,8 @@ void LLNearbyChat::float_panel() mResizeBar[LLResizeBar::LEFT]->setVisible(true); mResizeBar[LLResizeBar::RIGHT]->setVisible(true); + getDragHandle()->setVisible(true); + translate(4,4); } @@ -460,7 +478,7 @@ BOOL LLNearbyChat::handleRightMouseDown(S32 x, S32 y, MASK mask) void LLNearbyChat::onOpen(const LLSD& key ) { - LLNotificationsUI::LLScreenChannel* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->getChannelByID(LLUUID(NEARBY_CHAT_ID)); + LLNotificationsUI::LLScreenChannel* chat_channel = LLNotificationsUI::LLChannelManager::getInstance()->getChannelByID(LLUUID(gSavedSettings.getString("NearByChatChannelUUID"))); if(chat_channel) { chat_channel->removeToastsFromChannel(); diff --git a/indra/newview/llnearbychatbar.cpp b/indra/newview/llnearbychatbar.cpp index 50e31e85e4..d4a9be0355 100644 --- a/indra/newview/llnearbychatbar.cpp +++ b/indra/newview/llnearbychatbar.cpp @@ -65,7 +65,7 @@ LLGestureComboBox::LLGestureComboBox(const LLGestureComboBox::Params& p) , mGestureLabelTimer() , mLabel(p.label) { - setCommitCallback(boost::bind(&LLGestureComboBox::onCommitGesture, this, _1)); + setCommitCallback(boost::bind(&LLGestureComboBox::onCommitGesture, this)); // now register us as observer since we have a place to put the results LLGestureManager::instance().addObserver(this); @@ -82,41 +82,33 @@ LLGestureComboBox::~LLGestureComboBox() void LLGestureComboBox::refreshGestures() { //store current selection so we can maintain it - std::string cur_gesture = getValue().asString(); + LLSD cur_gesture = getValue(); selectFirstItem(); // clear clearRows(); + mGestures.clear(); - // collect list of unique gestures - std::map <std::string, BOOL> unique; LLGestureManager::item_map_t::iterator it; + LLSD::Integer idx(0); for (it = LLGestureManager::instance().mActive.begin(); it != LLGestureManager::instance().mActive.end(); ++it) { LLMultiGesture* gesture = (*it).second; if (gesture) { - if (!gesture->mTrigger.empty()) - { - unique[gesture->mTrigger] = TRUE; - } + addSimpleElement(gesture->mName, ADD_BOTTOM, LLSD(idx)); + mGestures.push_back(gesture); + idx++; } } - // add unique gestures - std::map <std::string, BOOL>::iterator it2; - for (it2 = unique.begin(); it2 != unique.end(); ++it2) - { - addSimpleElement((*it2).first); - } - sortByName(); // Insert label after sorting, at top, with separator below it addSeparator(ADD_TOP); addSimpleElement(mLabel, ADD_TOP); - if (!cur_gesture.empty()) + if (cur_gesture.isDefined()) { - selectByValue(LLSD(cur_gesture)); + selectByValue(cur_gesture); } else { @@ -124,7 +116,7 @@ void LLGestureComboBox::refreshGestures() } } -void LLGestureComboBox::onCommitGesture(LLUICtrl* ctrl) +void LLGestureComboBox::onCommitGesture() { LLCtrlListInterface* gestures = getListInterface(); if (gestures) @@ -134,19 +126,16 @@ void LLGestureComboBox::onCommitGesture(LLUICtrl* ctrl) { return; } - const std::string& trigger = gestures->getSelectedValue().asString(); - // pretend the user chatted the trigger string, to invoke - // substitution and logging. - std::string text(trigger); - std::string revised_text; - LLGestureManager::instance().triggerAndReviseString(text, &revised_text); - - revised_text = utf8str_trim(revised_text); - if (!revised_text.empty()) + index = gestures->getSelectedValue().asInteger(); + LLMultiGesture* gesture = mGestures.at(index); + if(gesture) { - // Don't play nodding animation - LLNearbyChatBar::sendChatFromViewer(revised_text, CHAT_TYPE_NORMAL, FALSE); + LLGestureManager::instance().playGesture(gesture); + if(!gesture->mReplaceText.empty()) + { + LLNearbyChatBar::sendChatFromViewer(gesture->mReplaceText, CHAT_TYPE_NORMAL, FALSE); + } } } @@ -267,9 +256,6 @@ void LLNearbyChatBar::onChatBoxKeystroke(LLLineEditor* caller, void* userdata) LLNearbyChatBar* self = (LLNearbyChatBar *)userdata; - if (!self->mChatBox) - return; - LLWString raw_text = self->mChatBox->getWText(); // Can't trim the end, because that will cause autocompletion @@ -429,7 +415,7 @@ void LLNearbyChatBar::sendChat( EChatType type ) void LLNearbyChatBar::onChatBoxCommit() { - if (mChatBox && mChatBox->getText().length() > 0) + if (mChatBox->getText().length() > 0) { sendChat(CHAT_TYPE_NORMAL); } @@ -501,7 +487,7 @@ void LLNearbyChatBar::startChat(const char* line) LLNearbyChatBar* cb = bt->getNearbyChatBar(); - if (!cb || !cb->mChatBox) + if (!cb ) return; bt->setVisible(TRUE); @@ -527,7 +513,7 @@ void LLNearbyChatBar::stopChat() LLNearbyChatBar* cb = bt->getNearbyChatBar(); - if (!cb || !cb->mChatBox) + if (!cb) return; cb->mChatBox->setFocus(FALSE); @@ -616,7 +602,7 @@ public: // Your code here bool handle(const LLSD& tokens, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { if (tokens.size() < 2) return false; S32 channel = tokens[0].asInteger(); diff --git a/indra/newview/llnearbychatbar.h b/indra/newview/llnearbychatbar.h index 4b0c42c3c0..19177e37b3 100644 --- a/indra/newview/llnearbychatbar.h +++ b/indra/newview/llnearbychatbar.h @@ -53,7 +53,7 @@ public: ~LLGestureComboBox(); void refreshGestures(); - void onCommitGesture(LLUICtrl* ctrl); + void onCommitGesture(); virtual void draw(); // LLGestureManagerObserver trigger @@ -61,6 +61,7 @@ public: protected: LLFrameTimer mGestureLabelTimer; + std::vector<LLMultiGesture*> mGestures; std::string mLabel; }; diff --git a/indra/newview/llnearbychathandler.cpp b/indra/newview/llnearbychathandler.cpp index 837f924c44..a1912655a3 100644 --- a/indra/newview/llnearbychathandler.cpp +++ b/indra/newview/llnearbychathandler.cpp @@ -52,7 +52,7 @@ LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& i LLNearbyChat* nearby_chat = LLFloaterReg::getTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); ///////////////////////////////////////////////////// LLChannelManager::Params p; //TODO: check and correct - p.id = LLUUID(NEARBY_CHAT_ID); + p.id = LLUUID(gSavedSettings.getString("NearByChatChannelUUID")); p.channel_right_bound = nearby_chat->getRect().mRight; p.channel_width = nearby_chat->getRect().mRight - 16; //HACK: 16 - ? ///////////////////////////////////////////////////// @@ -62,13 +62,14 @@ LLNearbyChatHandler::LLNearbyChatHandler(e_notification_type type, const LLSD& i mChannel = LLChannelManager::getInstance()->createChannel(p); mChannel->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_TOP); mChannel->setOverflowFormatString("You have %d unread nearby chat messages"); - mChannel->setCanStoreToasts(false); } LLNearbyChatHandler::~LLNearbyChatHandler() { } void LLNearbyChatHandler::processChat(const LLChat& chat_msg) { + if(chat_msg.mMuted == TRUE) + return; if(chat_msg.mSourceType == CHAT_SOURCE_AGENT && chat_msg.mFromID.notNull()) LLRecentPeople::instance().add(chat_msg.mFromID); @@ -96,20 +97,15 @@ void LLNearbyChatHandler::processChat(const LLChat& chat_msg) LLToast::Params p; p.id = id; p.panel = item; - p.on_mouse_enter = boost::bind(&LLNearbyChatHandler::onToastDestroy, this, _1); + p.on_toast_destroy = boost::bind(&LLNearbyChatHandler::onToastDestroy, this, _1); + p.on_mouse_enter = boost::bind(&LLNearbyChatHandler::removeNearbyToastsAndShowChat, this); mChannel->addToast(p); } void LLNearbyChatHandler::onToastDestroy(LLToast* toast) { - //TODO: what should be done to toasts here? may be htey are to be destroyed? - //toast->hide(); - if(mChannel) - mChannel->removeToastsFromChannel(); - else if(toast) - toast->hide(); - - LLFloaterReg::showTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + if(toast) + toast->closeFloater(); } void LLNearbyChatHandler::onChicletClick(void) @@ -117,7 +113,16 @@ void LLNearbyChatHandler::onChicletClick(void) } void LLNearbyChatHandler::onChicletClose(void) { +} +void LLNearbyChatHandler::removeNearbyToastsAndShowChat() +{ + /* + if(mChannel) + mChannel->removeToastsFromChannel(); + + LLFloaterReg::showTypedInstance<LLNearbyChat>("nearby_chat", LLSD()); + */ } } diff --git a/indra/newview/llnearbychathandler.h b/indra/newview/llnearbychathandler.h index 89ac08b9f0..8fcd03689d 100644 --- a/indra/newview/llnearbychathandler.h +++ b/indra/newview/llnearbychathandler.h @@ -35,8 +35,6 @@ #include "llnotificationhandler.h" -#define NEARBY_CHAT_ID "E1158BD6-661C-4981-9DAD-4DCBFF062502" - //add LLNearbyChatHandler to LLNotificationsUI namespace namespace LLNotificationsUI{ @@ -53,6 +51,7 @@ public: virtual void onChicletClose(void); protected: + void removeNearbyToastsAndShowChat(); }; } diff --git a/indra/newview/llnotificationalerthandler.cpp b/indra/newview/llnotificationalerthandler.cpp index 7003879dbf..bd6c6b2308 100644 --- a/indra/newview/llnotificationalerthandler.cpp +++ b/indra/newview/llnotificationalerthandler.cpp @@ -49,14 +49,16 @@ LLAlertHandler::LLAlertHandler(e_notification_type type, const LLSD& id) : mIsMo LLBottomTray* tray = LLBottomTray::getInstance(); LLChannelManager::Params p; - p.id = LLUUID(ALERT_CHANNEL_ID); + p.id = LLUUID(gSavedSettings.getString("AlertChannelUUID")); p.channel_right_bound = tray->getRect().getWidth() / 2; p.channel_width = 0; + p.display_toasts_always = true; p.align = NA_CENTRE; // Getting a Channel for our notifications mChannel = LLChannelManager::getInstance()->createChannel(p); mChannel->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP); + mChannel->setShowToasts(true); } //-------------------------------------------------------------------------- @@ -97,7 +99,7 @@ void LLAlertHandler::processNotification(const LLSD& notify) void LLAlertHandler::onToastDestroy(LLToast* toast) { - toast->close(); + toast->closeFloater(); } //-------------------------------------------------------------------------- diff --git a/indra/newview/llnotificationgrouphandler.cpp b/indra/newview/llnotificationgrouphandler.cpp index 9a6a041c35..31753efec9 100644 --- a/indra/newview/llnotificationgrouphandler.cpp +++ b/indra/newview/llnotificationgrouphandler.cpp @@ -34,8 +34,8 @@ #include "llnotificationhandler.h" #include "lltoastgroupnotifypanel.h" -#include "llagent.h" #include "llbottomtray.h" +#include "llgroupactions.h" #include "llviewercontrol.h" #include "llfloaterreg.h" #include "llsyswellwindow.h" @@ -51,7 +51,7 @@ LLGroupHandler::LLGroupHandler(e_notification_type type, const LLSD& id) LLBottomTray* tray = LLBottomTray::getInstance(); mChiclet = tray->getSysWell(); LLChannelManager::Params p; - p.chiclet = mChiclet; + p.id = LLUUID(gSavedSettings.getString("NotificationChannelUUID")); p.channel_right_bound = tray->getRect().mRight - gSavedSettings.getS32("NotificationChannelRightMargin"); p.channel_width = gSavedSettings.getS32("NotifyBoxWidth"); @@ -77,7 +77,10 @@ void LLGroupHandler::processNotification(const LLSD& notify) p.panel = notify_box; p.on_toast_destroy = boost::bind(&LLGroupHandler::onToastDestroy, this, _1); mChannel->addToast(p); - mChiclet->setCounter(mChiclet->getCounter() + 1); + static_cast<LLNotificationChiclet*>(mChiclet)->incUreadSystemNotifications(); + + LLGroupActions::refresh_notices(); + } else if (notify["sigtype"].asString() == "delete") { @@ -88,7 +91,7 @@ void LLGroupHandler::processNotification(const LLSD& notify) //-------------------------------------------------------------------------- void LLGroupHandler::onToastDestroy(LLToast* toast) { - mChiclet->setCounter(mChiclet->getCounter() - 1); + static_cast<LLNotificationChiclet*>(mChiclet)->decUreadSystemNotifications(); LLToastPanel* panel = dynamic_cast<LLToastPanel*>(toast->getPanel()); LLFloaterReg::getTypedInstance<LLSysWellWindow>("syswell_window")->removeItemByID(panel->getID()); @@ -97,7 +100,7 @@ void LLGroupHandler::onToastDestroy(LLToast* toast) if(toast->hasFocus()) mChannel->setHovering(false); - toast->close(); + toast->closeFloater(); } //-------------------------------------------------------------------------- @@ -113,14 +116,3 @@ void LLGroupHandler::onChicletClose(void) //-------------------------------------------------------------------------- -//-------------------------------------------------------------------------- - - -//-------------------------------------------------------------------------- - - -//-------------------------------------------------------------------------- - - -//-------------------------------------------------------------------------- - diff --git a/indra/newview/llnotificationhandler.h b/indra/newview/llnotificationhandler.h index 2e5fdd9ed5..6982ab7096 100644 --- a/indra/newview/llnotificationhandler.h +++ b/indra/newview/llnotificationhandler.h @@ -42,9 +42,6 @@ namespace LLNotificationsUI { -// ID for channel that displays Alert Notifications -#define ALERT_CHANNEL_ID "F3E07BC8-A973-476D-8C7F-F3B7293975D1" - // ENotificationType enumerates all possible types of notifications that could be met // typedef enum e_notification_type @@ -118,6 +115,25 @@ public: }; /** + * Handler for IM notifications. + * It manages life time of tip and script notices. + */ +class LLIMHandler : public LLSysHandler +{ +public: + LLIMHandler(); + virtual ~LLIMHandler(); + + // base interface functions + virtual void processNotification(const LLSD& notify); + virtual void onToastDestroy(LLToast* toast); + virtual void onChicletClick(void); + virtual void onChicletClose(void); + +protected: +}; + +/** * Handler for system informational notices. * It manages life time of tip and script notices. */ @@ -135,6 +151,7 @@ public: // own handlers void onStoreToast(LLPanel* info_panel, LLUUID id); + void onRejectToast(LLToast::Params p); protected: }; diff --git a/indra/newview/llnotificationmanager.cpp b/indra/newview/llnotificationmanager.cpp index 3eda0d0d14..31266fdecf 100644 --- a/indra/newview/llnotificationmanager.cpp +++ b/indra/newview/llnotificationmanager.cpp @@ -62,18 +62,21 @@ void LLNotificationManager::init() LLNotificationChannel::buildChannel("Group Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "groupnotify")); LLNotificationChannel::buildChannel("Alerts", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alert")); LLNotificationChannel::buildChannel("AlertModal", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "alertmodal")); + LLNotificationChannel::buildChannel("IM Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytoast")); LLNotifications::instance().getChannel("Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); LLNotifications::instance().getChannel("NotificationTips")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); LLNotifications::instance().getChannel("Group Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); LLNotifications::instance().getChannel("Alerts")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); LLNotifications::instance().getChannel("AlertModal")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); + LLNotifications::instance().getChannel("IM Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1)); mNotifyHandlers["notify"] = boost::shared_ptr<LLEventHandler>(new LLInfoHandler(NT_NOTIFY, LLSD())); mNotifyHandlers["notifytip"] = mNotifyHandlers["notify"]; mNotifyHandlers["groupnotify"] = boost::shared_ptr<LLEventHandler>(new LLGroupHandler(NT_GROUPNOTIFY, LLSD())); mNotifyHandlers["alert"] = boost::shared_ptr<LLEventHandler>(new LLAlertHandler(NT_ALERT, LLSD())); mNotifyHandlers["alertmodal"] = mNotifyHandlers["alert"]; + mNotifyHandlers["notifytoast"] = boost::shared_ptr<LLEventHandler>(new LLIMHandler()); mNotifyHandlers["nearbychat"] = boost::shared_ptr<LLEventHandler>(new LLNearbyChatHandler(NT_NEARBYCHAT, LLSD())); } diff --git a/indra/newview/lloverlaybar.cpp b/indra/newview/lloverlaybar.cpp index 0eb96f992a..ea24638b6d 100644 --- a/indra/newview/lloverlaybar.cpp +++ b/indra/newview/lloverlaybar.cpp @@ -37,7 +37,7 @@ #include "lloverlaybar.h" -#include "audioengine.h" +#include "llaudioengine.h" #include "llrender.h" #include "llagent.h" #include "llbutton.h" @@ -60,7 +60,7 @@ #include "llvoiceclient.h" #include "llvoavatarself.h" #include "llvoiceremotectrl.h" -#include "llwebbrowserctrl.h" +#include "llmediactrl.h" #include "llselectmgr.h" // @@ -315,7 +315,7 @@ void LLOverlayBar::mediaStop(void*) { if (!gOverlayBar) { - return; + // return; } LLViewerParcelMedia::stop(); } @@ -324,15 +324,15 @@ void LLOverlayBar::toggleMediaPlay(void*) { if (!gOverlayBar) { - return; + // return; } - if (LLViewerMedia::isMediaPaused()) + if (LLViewerParcelMedia::getStatus() == LLViewerMediaImpl::MEDIA_PAUSED) { LLViewerParcelMedia::start(); } - else if(LLViewerMedia::isMediaPlaying()) + else if(LLViewerParcelMedia::getStatus() == LLViewerMediaImpl::MEDIA_PLAYING) { LLViewerParcelMedia::pause(); } diff --git a/indra/newview/llpanelavatar.cpp b/indra/newview/llpanelavatar.cpp index 6c7fb8a0be..6e94b087a6 100644 --- a/indra/newview/llpanelavatar.cpp +++ b/indra/newview/llpanelavatar.cpp @@ -369,7 +369,6 @@ void LLPanelAvatarProfile::resetControls() childSetVisible("sl_groups", true); childSetEnabled("add_friend", true); - childSetVisible("user_name", false); childSetVisible("status_me_panel", false); childSetVisible("profile_me_buttons_panel", false); childSetVisible("account_actions_panel", false); @@ -609,10 +608,6 @@ BOOL LLPanelAvatarMeProfile::postBuild() void LLPanelAvatarMeProfile::onOpen(const LLSD& key) { LLPanelProfileTab::onOpen(key); - - std::string full_name; - gCacheName->getFullName(getAvatarId(), full_name); - childSetValue("user_name", full_name); } void LLPanelAvatarMeProfile::processProfileProperties(const LLAvatarData* avatar_data) @@ -647,7 +642,6 @@ void LLPanelAvatarMeProfile::fillStatusData(const LLAvatarData* avatar_data) void LLPanelAvatarMeProfile::resetControls() { - childSetVisible("user_name", true); childSetVisible("status_panel", false); childSetVisible("profile_buttons_panel", false); childSetVisible("title_groups_text", false); diff --git a/indra/newview/llpanelavatar.h b/indra/newview/llpanelavatar.h index a6cf2a2d27..51bd619901 100644 --- a/indra/newview/llpanelavatar.h +++ b/indra/newview/llpanelavatar.h @@ -75,6 +75,16 @@ public: virtual void onOpen(const LLSD& key); /** + * Profile tabs should close any opened panels here. + * + * Called from LLPanelProfile::onOpen() before opening new profile. + * See LLPanelpicks::onClose for example. LLPanelPicks closes picture info panel + * before new profile is displayed, otherwise new profile will + * be hidden behind picture info panel. + */ + virtual void onClose() {} + + /** * Resets controls visibility, state, etc. */ virtual void resetControls(){}; diff --git a/indra/newview/llpanelclassified.cpp b/indra/newview/llpanelclassified.cpp index 42522942f3..c77d089af7 100644 --- a/indra/newview/llpanelclassified.cpp +++ b/indra/newview/llpanelclassified.cpp @@ -57,8 +57,8 @@ #include "lllineeditor.h" #include "lltextbox.h" #include "llcombobox.h" -#include "llviewertexteditor.h" #include "lltexturectrl.h" +#include "lltexteditor.h" #include "lluiconstants.h" #include "llurldispatcher.h" // for classified HTML detail click teleports #include "lluictrlfactory.h" @@ -142,7 +142,7 @@ public: const bool from_search = true; LLPanelClassified::sendClassifiedClickMessage(classified_id, "teleport", from_search); // Invoke teleport - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; const bool trusted_browser = true; return LLURLDispatcher::dispatch(url, web, trusted_browser); } @@ -246,8 +246,7 @@ BOOL LLPanelClassified::postBuild() mDescEditor->setCommitOnFocusLost(TRUE); mDescEditor->setFocusReceivedCallback(focusReceived, this); mDescEditor->setCommitCallback(onCommitAny, this); - mDescEditor->setTabsToNextField(TRUE); - + mLocationEditor = getChild<LLLineEditor>("location_editor"); mSetBtn = getChild<LLButton>( "set_location_btn"); diff --git a/indra/newview/llpanelface.cpp b/indra/newview/llpanelface.cpp index e9e71644b1..08a50d4b6e 100644 --- a/indra/newview/llpanelface.cpp +++ b/indra/newview/llpanelface.cpp @@ -62,6 +62,7 @@ #include "llviewerobject.h" #include "llviewerstats.h" #include "lluictrlfactory.h" +#include "llpluginclassmedia.h" // // Methods @@ -386,11 +387,6 @@ void LLPanelFace::getState() childSetEnabled("button align",FALSE); //mBtnAutoFix->setEnabled ( FALSE ); - if(LLViewerMedia::hasMedia()) - { - childSetEnabled("textbox autofix",editable); - childSetEnabled("button align",editable); - } //if ( LLMediaEngine::getInstance()->getMediaRenderer () ) // if ( LLMediaEngine::getInstance()->getMediaRenderer ()->isLoaded () ) // { @@ -447,7 +443,15 @@ void LLPanelFace::getState() } } } + + if(LLViewerMedia::textureHasMedia(id)) + { + childSetEnabled("textbox autofix",editable); + childSetEnabled("button align",editable); + } + } + LLAggregatePermissions texture_perms; if(texture_ctrl) @@ -919,14 +923,18 @@ struct LLPanelFaceSetMediaFunctor : public LLSelectedTEFunctor { virtual bool apply(LLViewerObject* object, S32 te) { + // TODO: the media impl pointer should actually be stored by the texture + viewer_media_t pMediaImpl = LLViewerMedia::getMediaImplFromTextureID(object->getTE ( te )->getID()); // only do this if it's a media texture - if ( object->getTE ( te )->getID() == LLViewerMedia::getMediaTextureID() ) + if ( pMediaImpl.notNull()) { - S32 media_width, media_height; - S32 texture_width, texture_height; - if ( LLViewerMedia::getMediaSize( &media_width, &media_height ) - && LLViewerMedia::getTextureSize( &texture_width, &texture_height ) ) + LLPluginClassMedia *media = pMediaImpl->getMediaPlugin(); + if(media) { + S32 media_width = media->getWidth(); + S32 media_height = media->getHeight(); + S32 texture_width = media->getTextureWidth(); + S32 texture_height = media->getTextureHeight(); F32 scale_s = (F32)media_width / (F32)texture_width; F32 scale_t = (F32)media_height / (F32)texture_height; diff --git a/indra/newview/llpanelgroup.cpp b/indra/newview/llpanelgroup.cpp index 086b06c1a3..2e87f0b65b 100644 --- a/indra/newview/llpanelgroup.cpp +++ b/indra/newview/llpanelgroup.cpp @@ -33,7 +33,6 @@ #include "llpanelgroup.h" -#include "llagent.h" #include "llbutton.h" #include "lltabcontainer.h" #include "lltextbox.h" @@ -116,6 +115,9 @@ LLPanelGroup::~LLPanelGroup() void LLPanelGroup::onOpen(const LLSD& key) { + if(!key.has("group_id")) + return; + LLUUID group_id = key["group_id"]; if(!key.has("action")) { @@ -127,7 +129,7 @@ void LLPanelGroup::onOpen(const LLSD& key) if(str_action == "refresh") { - if(mID == group_id) + if(mID == group_id || group_id == LLUUID::null) refreshData(); } else if(str_action == "close") @@ -138,6 +140,12 @@ void LLPanelGroup::onOpen(const LLSD& key) { setGroupID(LLUUID::null); } + else if(str_action == "refresh_notices") + { + LLPanelGroupNotices* panel_notices = findChild<LLPanelGroupNotices>("group_notices_tab_panel"); + if(panel_notices) + panel_notices->refreshNotices(); + } } @@ -180,21 +188,32 @@ void LLPanelGroup::reshape(S32 width, S32 height, BOOL called_from_parent ) { LLPanel::reshape(width, height, called_from_parent ); - LLButton* button = getChild<LLButton>("btn_apply"); - LLRect btn_rect = button->getRect(); - btn_rect.setLeftTopAndSize( btn_rect.mLeft, 28, btn_rect.getWidth(), btn_rect.getHeight()); - button->setRect(btn_rect); + LLRect btn_rect; - button = getChild<LLButton>("btn_create"); - btn_rect = button->getRect(); - btn_rect.setLeftTopAndSize( btn_rect.mLeft, 28, btn_rect.getWidth(), btn_rect.getHeight()); - button->setRect(btn_rect); + LLButton* button = findChild<LLButton>("btn_apply"); + if(button) + { + btn_rect = button->getRect(); + btn_rect.setLeftTopAndSize( btn_rect.mLeft, 28, btn_rect.getWidth(), btn_rect.getHeight()); + button->setRect(btn_rect); + } + button = findChild<LLButton>("btn_create"); + if(button) + { + btn_rect = button->getRect(); + btn_rect.setLeftTopAndSize( btn_rect.mLeft, 28, btn_rect.getWidth(), btn_rect.getHeight()); + button->setRect(btn_rect); + } - button = getChild<LLButton>("btn_refresh"); - btn_rect = button->getRect(); - btn_rect.setLeftTopAndSize( btn_rect.mLeft, 28, btn_rect.getWidth(), btn_rect.getHeight()); - button->setRect(btn_rect); + + button = findChild<LLButton>("btn_refresh"); + if(button) + { + btn_rect = button->getRect(); + btn_rect.setLeftTopAndSize( btn_rect.mLeft, 28, btn_rect.getWidth(), btn_rect.getHeight()); + button->setRect(btn_rect); + } } void LLPanelGroup::onBackBtnClick() @@ -246,6 +265,7 @@ void LLPanelGroup::notifyObservers() LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); if(gdatap) childSetValue("group_name", gdatap->mName); + } @@ -259,22 +279,31 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) for(std::vector<LLPanelGroupTab* >::iterator it = mTabs.begin();it!=mTabs.end();++it) (*it)->setGroupID(group_id); - LLButton* button_apply = getChild<LLButton>("btn_apply"); - LLButton* button_refresh = getChild<LLButton>("btn_refresh"); - LLButton* button_create = getChild<LLButton>("btn_create"); + LLGroupMgrGroupData* gdatap = LLGroupMgr::getInstance()->getGroupData(mID); + if(gdatap) + childSetValue("group_name", gdatap->mName); + + LLButton* button_apply = findChild<LLButton>("btn_apply"); + LLButton* button_refresh = findChild<LLButton>("btn_refresh"); + LLButton* button_create = findChild<LLButton>("btn_create"); bool is_null_group_id = group_id == LLUUID::null; - - button_apply->setVisible(!is_null_group_id); - button_refresh->setVisible(!is_null_group_id); - button_create->setVisible(is_null_group_id); + if(button_apply) + button_apply->setVisible(!is_null_group_id); + if(button_refresh) + button_refresh->setVisible(!is_null_group_id); + if(button_create) + button_create->setVisible(is_null_group_id); LLAccordionCtrlTab* tab_general = findChild<LLAccordionCtrlTab>("group_general_tab"); LLAccordionCtrlTab* tab_roles = findChild<LLAccordionCtrlTab>("group_roles_tab"); LLAccordionCtrlTab* tab_notices = findChild<LLAccordionCtrlTab>("group_notices_tab"); LLAccordionCtrlTab* tab_land = findChild<LLAccordionCtrlTab>("group_land_tab"); + if(!tab_general || !tab_roles || !tab_notices || !tab_land) + return; + if(is_null_group_id)//creating new group { if(!tab_general->getDisplayChildren()) @@ -293,6 +322,15 @@ void LLPanelGroup::setGroupID(const LLUUID& group_id) } else { + if(!tab_general->getDisplayChildren()) + tab_general->changeOpenClose(tab_general->getDisplayChildren()); + if(!tab_roles->getDisplayChildren()) + tab_roles->changeOpenClose(tab_roles->getDisplayChildren()); + if(!tab_notices->getDisplayChildren()) + tab_notices->changeOpenClose(tab_notices->getDisplayChildren()); + if(!tab_land->getDisplayChildren()) + tab_land->changeOpenClose(tab_land->getDisplayChildren()); + tab_roles->canOpenClose(true); tab_notices->canOpenClose(true); tab_land->canOpenClose(true); diff --git a/indra/newview/llpanelgroupgeneral.cpp b/indra/newview/llpanelgroupgeneral.cpp index d63e112357..2d06dcdb36 100644 --- a/indra/newview/llpanelgroupgeneral.cpp +++ b/indra/newview/llpanelgroupgeneral.cpp @@ -71,11 +71,9 @@ LLPanelGroupGeneral::LLPanelGroupGeneral() mChanged(FALSE), mFirstUse(TRUE), mGroupNameEditor(NULL), - mGroupName(NULL), mFounderName(NULL), mInsignia(NULL), mEditCharter(NULL), - mBtnJoinGroup(NULL), mListVisibleMembers(NULL), mCtrlShowInGroupList(NULL), mComboMature(NULL), @@ -100,7 +98,6 @@ BOOL LLPanelGroupGeneral::postBuild() // General info mGroupNameEditor = getChild<LLLineEditor>("group_name_editor", recurse); - mGroupName = getChild<LLTextBox>("group_name", recurse); mInsignia = getChild<LLTextureCtrl>("insignia", recurse); if (mInsignia) @@ -117,17 +114,6 @@ BOOL LLPanelGroupGeneral::postBuild() mEditCharter->setFocusChangedCallback(onFocusEdit, this); } - mBtnJoinGroup = getChild<LLButton>("join_button", recurse); - if ( mBtnJoinGroup ) - { - mBtnJoinGroup->setClickedCallback(onClickJoin, this); - } - - mBtnInfo = getChild<LLButton>("info_button", recurse); - if ( mBtnInfo ) - { - mBtnInfo->setClickedCallback(onClickInfo, this); - } mFounderName = getChild<LLNameBox>("founder_name"); @@ -224,9 +210,6 @@ BOOL LLPanelGroupGeneral::postBuild() mCtrlEnrollmentFee->setEnabled(TRUE); mSpinEnrollmentFee->setEnabled(TRUE); - mBtnJoinGroup->setVisible(FALSE); - mBtnInfo->setVisible(FALSE); - mGroupName->setVisible(FALSE); } return LLPanelGroupTab::postBuild(); @@ -692,28 +675,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) can_change_member_opts); mSpinEnrollmentFee->resetDirty(); } - if ( mBtnJoinGroup ) - { - std::string fee_buff; - bool visible; - - visible = !is_member && gdatap->mOpenEnrollment; - mBtnJoinGroup->setVisible(visible); - - if ( visible ) - { - LLStringUtil::format_map_t string_args; - string_args["[AMOUNT]"] = llformat("%d", gdatap->mMembershipFee); - fee_buff = getString("group_join_btn", string_args); - mBtnJoinGroup->setLabelSelected(fee_buff); - mBtnJoinGroup->setLabelUnselected(fee_buff); - } - } - if ( mBtnInfo ) - { - mBtnInfo->setVisible(is_member && !mAllowEdit); - } - if (mCtrlReceiveNotices) { mCtrlReceiveNotices->setVisible(is_member); @@ -728,7 +689,6 @@ void LLPanelGroupGeneral::update(LLGroupChange gc) if (mInsignia) mInsignia->setEnabled(mAllowEdit && can_change_ident); if (mEditCharter) mEditCharter->setEnabled(mAllowEdit && can_change_ident); - if (mGroupName) mGroupName->setText(gdatap->mName); if (mGroupNameEditor) mGroupNameEditor->setVisible(FALSE); if (mFounderName) mFounderName->setNameID(gdatap->mFounderID,FALSE); if (mInsignia) @@ -858,7 +818,6 @@ void LLPanelGroupGeneral::updateChanged() LLUICtrl *check_list[] = { mGroupNameEditor, - mGroupName, mFounderName, mInsignia, mEditCharter, @@ -887,6 +846,10 @@ void LLPanelGroupGeneral::updateChanged() void LLPanelGroupGeneral::reset() { + mFounderName->setVisible(false); + + getChild<LLUICtrl>("prepend_founded_by")->setVisible(false); + mCtrlReceiveNotices->set(false); @@ -910,10 +873,6 @@ void LLPanelGroupGeneral::reset() mSpinEnrollmentFee->setEnabled(TRUE); mSpinEnrollmentFee->set((F32)0); - mBtnJoinGroup->setVisible(FALSE); - mBtnInfo->setVisible(FALSE); - mGroupName->setVisible(FALSE); - mGroupNameEditor->setVisible(true); mComboActiveTitle->setVisible(false); @@ -933,7 +892,6 @@ void LLPanelGroupGeneral::reset() mListVisibleMembers->addElement(row); } - mBtnJoinGroup->setVisible(false); { mComboMature->setEnabled(true); @@ -950,7 +908,6 @@ void LLPanelGroupGeneral::resetDirty() LLUICtrl *check_list[] = { mGroupNameEditor, - mGroupName, mFounderName, mInsignia, mEditCharter, @@ -1010,21 +967,8 @@ void LLPanelGroupGeneral::setGroupID(const LLUUID& id) mComboActiveTitle = getChild<LLComboBox>("active_title"); - if (mGroupID.isNull()) - { - mGroupNameEditor->setEnabled(TRUE); - mEditCharter->setEnabled(TRUE); - - mCtrlShowInGroupList->setEnabled(TRUE); - mComboMature->setEnabled(TRUE); - mCtrlOpenEnrollment->setEnabled(TRUE); - mCtrlEnrollmentFee->setEnabled(TRUE); - mSpinEnrollmentFee->setEnabled(TRUE); - - mBtnJoinGroup->setVisible(FALSE); - mBtnInfo->setVisible(FALSE); - mGroupName->setVisible(FALSE); - } + mFounderName->setVisible(true); + getChild<LLUICtrl>("prepend_founded_by")->setVisible(true); resetDirty(); diff --git a/indra/newview/llpanelgroupgeneral.h b/indra/newview/llpanelgroupgeneral.h index e7028228b0..21d526f43d 100644 --- a/indra/newview/llpanelgroupgeneral.h +++ b/indra/newview/llpanelgroupgeneral.h @@ -96,12 +96,9 @@ private: // Group information (include any updates in updateChanged) LLLineEditor *mGroupNameEditor; - LLTextBox *mGroupName; LLNameBox *mFounderName; LLTextureCtrl *mInsignia; LLTextEditor *mEditCharter; - LLButton *mBtnJoinGroup; - LLButton *mBtnInfo; LLNameListCtrl *mListVisibleMembers; diff --git a/indra/newview/llpanelgroupinvite.cpp b/indra/newview/llpanelgroupinvite.cpp index 1e6eb8ed44..c5eaa34204 100644 --- a/indra/newview/llpanelgroupinvite.cpp +++ b/indra/newview/llpanelgroupinvite.cpp @@ -37,6 +37,7 @@ #include "llfloateravatarpicker.h" #include "llbutton.h" #include "llcombobox.h" +#include "llgroupactions.h" #include "llgroupmgr.h" #include "llnamelistctrl.h" #include "llscrolllistitem.h" @@ -80,6 +81,7 @@ public: LLButton *mRemoveButton; LLTextBox *mGroupName; std::string mOwnerWarning; + std::string mAlreadyInGroup; bool mConfirmedOwnerInvite; void (*mCloseCallback)(void* data); @@ -167,16 +169,29 @@ void LLPanelGroupInvite::impl::submitInvitations() } } + bool already_in_group = false; //loop over the users std::vector<LLScrollListItem*> items = mInvitees->getAllData(); for (std::vector<LLScrollListItem*>::iterator iter = items.begin(); iter != items.end(); ++iter) { LLScrollListItem* item = *iter; + if(LLGroupActions::isAvatarMemberOfGroup(mGroupID, item->getUUID())) + { + already_in_group = true; + continue; + } role_member_pairs[item->getUUID()] = role_id; } - + LLGroupMgr::getInstance()->sendGroupMemberInvites(mGroupID, role_member_pairs); + + if(already_in_group) + { + LLSD msg; + msg["MESSAGE"] = mAlreadyInGroup; + LLNotifications::instance().add("GenericAlert", msg); + } //then close (*mCloseCallback)(mCloseCallbackUserData); @@ -550,6 +565,7 @@ BOOL LLPanelGroupInvite::postBuild() } mImplementation->mOwnerWarning = getString("confirm_invite_owner_str"); + mImplementation->mAlreadyInGroup = getString("already_in_group"); update(); diff --git a/indra/newview/llpanelgrouplandmoney.cpp b/indra/newview/llpanelgrouplandmoney.cpp index d95240e30c..e40fa19bb6 100644 --- a/indra/newview/llpanelgrouplandmoney.cpp +++ b/indra/newview/llpanelgrouplandmoney.cpp @@ -50,6 +50,7 @@ #include "llscrolllistcell.h" #include "lltextbox.h" #include "lltabcontainer.h" +#include "lltexteditor.h" #include "lltrans.h" #include "lltransactiontypes.h" #include "lltrans.h" @@ -1514,16 +1515,15 @@ void LLPanelGroupLandMoney::setGroupID(const LLUUID& id) mImplementationp->mGroupOverLimitIconp->setVisible(FALSE); } - if ( !can_view ) + if ( mImplementationp->mGroupParcelsp ) { - if ( mImplementationp->mGroupParcelsp ) - { - mImplementationp->mGroupParcelsp->setCommentText( - mImplementationp->mCantViewParcelsText); - mImplementationp->mGroupParcelsp->setEnabled(FALSE); - } + mImplementationp->mGroupParcelsp->setEnabled(can_view); } + if ( !can_view && mImplementationp->mGroupParcelsp ) + { + mImplementationp->mGroupParcelsp->setEnabled(FALSE); + } LLButton* earlierp, *laterp; diff --git a/indra/newview/llpanelgrouplandmoney.h b/indra/newview/llpanelgrouplandmoney.h index 73c52cdf2e..0f275ea9a8 100644 --- a/indra/newview/llpanelgrouplandmoney.h +++ b/indra/newview/llpanelgrouplandmoney.h @@ -37,10 +37,6 @@ #include "llmap.h" #include "lluuid.h" -#include "llbutton.h" -#include "lltexteditor.h" -#include "llpanel.h" - class LLPanelGroupLandMoney : public LLPanelGroupTab { public: diff --git a/indra/newview/llpanelgroupnotices.cpp b/indra/newview/llpanelgroupnotices.cpp index f06342ebfc..0ce85818dd 100644 --- a/indra/newview/llpanelgroupnotices.cpp +++ b/indra/newview/llpanelgroupnotices.cpp @@ -41,6 +41,7 @@ #include "llinventorymodel.h" #include "llfloaterinventory.h" #include "llagent.h" +#include "llagentui.h" #include "lltooldraganddrop.h" #include "lllineeditor.h" @@ -379,13 +380,38 @@ void LLPanelGroupNotices::onClickSendMessage(void* data) self->mCreateMessage->getText(), self->mInventoryItem); + + //instantly add new notice. actual notice will be added after ferreshNotices call + LLUUID id = LLUUID::generateNewID(); + std::string subj = self->mCreateSubject->getText(); + std::string name ; + LLAgentUI::buildFullname(name); + U32 timestamp = 0; + + LLSD row; + row["id"] = id; + + row["columns"][0]["column"] = "icon"; + + row["columns"][1]["column"] = "subject"; + row["columns"][1]["value"] = subj; + + row["columns"][2]["column"] = "from"; + row["columns"][2]["value"] = name; + + row["columns"][3]["column"] = "date"; + row["columns"][3]["value"] = build_notice_date(timestamp); + + row["columns"][4]["column"] = "sort"; + row["columns"][4]["value"] = llformat( "%u", timestamp); + + self->mNoticesList->addElement(row, ADD_BOTTOM); + self->mCreateMessage->clear(); self->mCreateSubject->clear(); onClickRemoveAttachment(data); self->arrangeNoticeView(VIEW_PAST_NOTICE); - onClickRefreshNotices(self); - } //static @@ -407,6 +433,26 @@ void LLPanelGroupNotices::onClickNewMessage(void* data) self->mNoticesList->deselectAllItems(TRUE); // TRUE == don't commit on chnage } +void LLPanelGroupNotices::refreshNotices() +{ + onClickRefreshNotices(this); + /* + lldebugs << "LLPanelGroupNotices::onClickGetPastNotices" << llendl; + + mNoticesList->deleteAllItems(); + + LLMessageSystem* msg = gMessageSystem; + msg->newMessage("GroupNoticesListRequest"); + msg->nextBlock("AgentData"); + msg->addUUID("AgentID",gAgent.getID()); + msg->addUUID("SessionID",gAgent.getSessionID()); + msg->nextBlock("Data"); + msg->addUUID("GroupID",self->mGroupID); + gAgent.sendReliableMessage(); + */ + +} + void LLPanelGroupNotices::onClickRefreshNotices(void* data) { lldebugs << "LLPanelGroupNotices::onClickGetPastNotices" << llendl; @@ -508,7 +554,7 @@ void LLPanelGroupNotices::processNotices(LLMessageSystem* msg) mNoticesList->addElement(row, ADD_BOTTOM); } - mNoticesList->sortItems(); + mNoticesList->updateSort(); } void LLPanelGroupNotices::onSelectNotice(LLUICtrl* ctrl, void* data) diff --git a/indra/newview/llpanelgroupnotices.h b/indra/newview/llpanelgroupnotices.h index c41a5f501b..4bda38c897 100644 --- a/indra/newview/llpanelgroupnotices.h +++ b/indra/newview/llpanelgroupnotices.h @@ -69,6 +69,8 @@ public: const std::string& inventory_name, LLOfferInfo* inventory_offer); + void refreshNotices(); + virtual void setGroupID(const LLUUID& id); private: diff --git a/indra/newview/llpanelgrouproles.cpp b/indra/newview/llpanelgrouproles.cpp index ab614fea53..4618b49df4 100644 --- a/indra/newview/llpanelgrouproles.cpp +++ b/indra/newview/llpanelgrouproles.cpp @@ -49,6 +49,7 @@ #include "lltabcontainer.h" #include "lltextbox.h" #include "lltexteditor.h" +#include "llsearcheditor.h" #include "llviewertexturelist.h" #include "llviewerwindow.h" #include "llfocusmgr.h" @@ -464,9 +465,7 @@ LLPanelGroupSubTab::LLPanelGroupSubTab() : LLPanelGroupTab(), mHeader(NULL), mFooter(NULL), - mSearchLineEditor(NULL), - mSearchButton(NULL), - mShowAllButton(NULL) + mSearchEditor(NULL) { } @@ -478,22 +477,14 @@ BOOL LLPanelGroupSubTab::postBuild() { // Hook up the search widgets. bool recurse = true; - mSearchLineEditor = getChild<LLLineEditor>("search_text", recurse); + mSearchEditor = getChild<LLSearchEditor>("filter_input", recurse); - if (!mSearchLineEditor) return FALSE; - mSearchLineEditor->setKeystrokeCallback(onSearchKeystroke, this); - - mSearchButton = getChild<LLButton>("search_button", recurse); - - if (!mSearchButton) return FALSE; - mSearchButton->setClickedCallback(onClickSearch, this); - mSearchButton->setEnabled(FALSE); + if (!mSearchEditor) + return FALSE; - mShowAllButton = getChild<LLButton>("show_all_button", recurse); + mSearchEditor->setCommitCallback(boost::bind(&LLPanelGroupSubTab::onClickSearch, this)); + mSearchEditor->setKeystrokeCallback(onSearchKeystroke, this); - if (!mShowAllButton) return FALSE; - mShowAllButton->setClickedCallback(onClickShowAll, this); - mShowAllButton->setEnabled(FALSE); // Get icons for later use. mActionIcons.clear(); @@ -516,63 +507,35 @@ BOOL LLPanelGroupSubTab::postBuild() return LLPanelGroupTab::postBuild(); } -// static -void LLPanelGroupSubTab::onSearchKeystroke(LLLineEditor* caller, void* user_data) -{ - LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data); - self->handleSearchKeystroke(caller); -} - -void LLPanelGroupSubTab::handleSearchKeystroke(LLLineEditor* caller) +void LLPanelGroupSubTab::setGroupID(const LLUUID& id) { - if (caller->getText().size()) - { - setDefaultBtn( mSearchButton ); - mSearchButton->setEnabled(TRUE); - } - else + LLPanelGroupTab::setGroupID(id); + if(mSearchEditor) { - setDefaultBtn( NULL ); - mSearchButton->setEnabled(FALSE); + mSearchEditor->clear(); + setSearchFilter(""); } } -// static -void LLPanelGroupSubTab::onClickSearch(void* user_data) +// static +void LLPanelGroupSubTab::onSearchKeystroke(LLLineEditor* caller, void* user_data) { LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data); - self->handleClickSearch(); -} - -void LLPanelGroupSubTab::handleClickSearch() -{ - lldebugs << "LLPanelGroupSubTab::handleClickSearch()" << llendl; - - if (0 == mSearchLineEditor->getText().size()) - { - // No search text. (This shouldn't happen... the search button should have been disabled). - llwarns << "handleClickSearch with no search text!" << llendl; - mSearchButton->setEnabled(FALSE); - return; - } + self->handleSearchKeystroke(caller); - setSearchFilter( mSearchLineEditor->getText() ); - mShowAllButton->setEnabled(TRUE); } -// static -void LLPanelGroupSubTab::onClickShowAll(void* user_data) +void LLPanelGroupSubTab::handleSearchKeystroke(LLLineEditor* caller) { - LLPanelGroupSubTab* self = static_cast<LLPanelGroupSubTab*>(user_data); - self->handleClickShowAll(); + setSearchFilter( caller->getText() ); } -void LLPanelGroupSubTab::handleClickShowAll() +// static +void LLPanelGroupSubTab::onClickSearch() { - lldebugs << "LLPanelGroupSubTab::handleClickShowAll()" << llendl; - setSearchFilter( LLStringUtil::null ); - mShowAllButton->setEnabled(FALSE); + setSearchFilter( mSearchEditor->getText() ); } + void LLPanelGroupSubTab::setSearchFilter(const std::string& filter) { @@ -892,6 +855,12 @@ BOOL LLPanelGroupMembersSubTab::postBuildSubTab(LLView* root) return TRUE; } +void LLPanelGroupMembersSubTab::setGroupID(const LLUUID& id) +{ + LLPanelGroupSubTab::setGroupID(id); + +} + // static void LLPanelGroupMembersSubTab::onMemberSelect(LLUICtrl* ctrl, void* user_data) @@ -2583,19 +2552,15 @@ void LLPanelGroupRoles::setGroupID(const LLUUID& id) if(group_members_tab) group_members_tab->setGroupID(id); if(group_roles_tab) group_roles_tab->setGroupID(id); if(group_actions_tab) group_actions_tab->setGroupID(id); - - activate(); - if (!mSubTabContainer) return ; + LLButton* button = getChild<LLButton>("member_invite"); + if ( button ) + button->setEnabled(gAgent.hasPowerInGroup(mGroupID, GP_MEMBER_INVITE)); - // Hook up each sub-tabs callback and widgets. - for (S32 i = 0; i < mSubTabContainer->getTabCount(); ++i) - { - LLPanel* panel = mSubTabContainer->getPanelByIndex(i); - LLPanelGroupSubTab* subtabp = dynamic_cast<LLPanelGroupSubTab*>(panel); - if (subtabp) - subtabp->postBuildSubTab(this); - } + if(mSubTabContainer) + mSubTabContainer->selectTab(0); + + activate(); } diff --git a/indra/newview/llpanelgrouproles.h b/indra/newview/llpanelgrouproles.h index 9519263bba..2a0f31fa0f 100644 --- a/indra/newview/llpanelgrouproles.h +++ b/indra/newview/llpanelgrouproles.h @@ -43,6 +43,7 @@ class LLPanelGroupActionsSubTab; class LLScrollListCtrl; class LLScrollListItem; class LLTextEditor; +class LLSearchEditor; // Forward declare for friend usage. //virtual BOOL LLPanelGroupSubTab::postBuildSubTab(LLView*); @@ -113,10 +114,7 @@ public: static void onSearchKeystroke(LLLineEditor* caller, void* user_data); void handleSearchKeystroke(LLLineEditor* caller); - static void onClickSearch(void*); - void handleClickSearch(); - static void onClickShowAll(void*); - void handleClickShowAll(); + void onClickSearch(); virtual void setSearchFilter( const std::string& filter ); @@ -144,13 +142,13 @@ public: BOOL is_owner_role); void setFooterEnabled(BOOL enable); + + virtual void setGroupID(const LLUUID& id); protected: LLPanel* mHeader; LLPanel* mFooter; - LLLineEditor* mSearchLineEditor; - LLButton* mSearchButton; - LLButton* mShowAllButton; + LLSearchEditor* mSearchEditor; std::string mSearchFilter; @@ -196,6 +194,8 @@ public: virtual void draw(); + virtual void setGroupID(const LLUUID& id); + protected: typedef std::map<LLUUID, LLRoleMemberChangeType> role_change_data_map_t; typedef std::map<LLUUID, role_change_data_map_t*> member_role_changes_map_t; diff --git a/indra/newview/llpanelimcontrolpanel.cpp b/indra/newview/llpanelimcontrolpanel.cpp index 45fe625a13..51cdc5af93 100644 --- a/indra/newview/llpanelimcontrolpanel.cpp +++ b/indra/newview/llpanelimcontrolpanel.cpp @@ -37,11 +37,9 @@ #include "llavataractions.h" #include "llavatariconctrl.h" #include "llbutton.h" - -static LLRegisterPanelClassWrapper<LLPanelIMControlPanel> t_im_control_panel("panel_im_control_panel"); +#include "llgroupactions.h" LLPanelIMControlPanel::LLPanelIMControlPanel() -: LLPanel() { } @@ -55,6 +53,7 @@ BOOL LLPanelIMControlPanel::postBuild() childSetAction("add_friend_btn", boost::bind(&LLPanelIMControlPanel::onAddFriendButtonClicked, this)); childSetAction("call_btn", boost::bind(&LLPanelIMControlPanel::onCallButtonClicked, this)); childSetAction("share_btn", boost::bind(&LLPanelIMControlPanel::onShareButtonClicked, this)); + childSetEnabled("add_friend_btn", !LLAvatarActions::isFriend(getChild<LLAvatarIconCtrl>("avatar_icon")->getAvatarId())); return TRUE; } @@ -81,7 +80,34 @@ void LLPanelIMControlPanel::onShareButtonClicked() // *TODO: Implement } -void LLPanelIMControlPanel::setAvatarId(const LLUUID& avatar_id) +void LLPanelIMControlPanel::setID(const LLUUID& avatar_id) { getChild<LLAvatarIconCtrl>("avatar_icon")->setValue(avatar_id); } + + + +BOOL LLPanelGroupControlPanel::postBuild() +{ + childSetAction("group_info_btn", boost::bind(&LLPanelGroupControlPanel::onGroupInfoButtonClicked, this)); + childSetAction("call_btn", boost::bind(&LLPanelGroupControlPanel::onCallButtonClicked, this)); + + return TRUE; +} + +void LLPanelGroupControlPanel::onGroupInfoButtonClicked() +{ + LLGroupActions::show(mGroupID); +} + + +void LLPanelGroupControlPanel::onCallButtonClicked() +{ + // *TODO: Implement +} + + +void LLPanelGroupControlPanel::setID(const LLUUID& id) +{ + mGroupID = id; +} diff --git a/indra/newview/llpanelimcontrolpanel.h b/indra/newview/llpanelimcontrolpanel.h index be3b2d3130..e82942a31d 100644 --- a/indra/newview/llpanelimcontrolpanel.h +++ b/indra/newview/llpanelimcontrolpanel.h @@ -35,7 +35,19 @@ #include "llpanel.h" -class LLPanelIMControlPanel : public LLPanel + +class LLPanelChatControlPanel : public LLPanel +{ +public: + LLPanelChatControlPanel() {}; + ~LLPanelChatControlPanel() {}; + + // sets the group or avatar UUID + virtual void setID(const LLUUID& avatar_id)= 0; +}; + + +class LLPanelIMControlPanel : public LLPanelChatControlPanel { public: LLPanelIMControlPanel(); @@ -43,7 +55,7 @@ public: BOOL postBuild(); - void setAvatarId(const LLUUID& avatar_id); + void setID(const LLUUID& avatar_id); private: void onViewProfileButtonClicked(); @@ -52,4 +64,24 @@ private: void onShareButtonClicked(); }; + +class LLPanelGroupControlPanel : public LLPanelChatControlPanel +{ +public: + LLPanelGroupControlPanel() {}; + ~LLPanelGroupControlPanel() {}; + + BOOL postBuild(); + + void setID(const LLUUID& id); + +private: + void onGroupInfoButtonClicked(); + void onCallButtonClicked(); + + LLUUID mGroupID; +}; + + + #endif // LL_LLPANELIMCONTROLPANEL_H diff --git a/indra/newview/llpanellandaudio.cpp b/indra/newview/llpanellandaudio.cpp new file mode 100644 index 0000000000..920fca66f2 --- /dev/null +++ b/indra/newview/llpanellandaudio.cpp @@ -0,0 +1,192 @@ +/** + * @file llpanellandaudio.cpp + * @brief Allows configuration of "media" for a land parcel, + * for example movies, web pages, and audio. + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llviewerprecompiledheaders.h" + +#include "llpanellandaudio.h" + +// viewer includes +#include "llmimetypes.h" +#include "llviewerparcelmgr.h" +#include "lluictrlfactory.h" + +// library includes +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llfloaterurlentry.h" +#include "llfocusmgr.h" +#include "lllineeditor.h" +#include "llparcel.h" +#include "lltextbox.h" +#include "llradiogroup.h" +#include "llspinctrl.h" +#include "llsdutil.h" +#include "lltexturectrl.h" +#include "roles_constants.h" +#include "llscrolllistctrl.h" + +// Values for the parcel voice settings radio group +enum +{ + kRadioVoiceChatEstate = 0, + kRadioVoiceChatPrivate = 1, + kRadioVoiceChatDisable = 2 +}; + +//--------------------------------------------------------------------------- +// LLPanelLandAudio +//--------------------------------------------------------------------------- + +LLPanelLandAudio::LLPanelLandAudio(LLParcelSelectionHandle& parcel) +: LLPanel(/*std::string("land_media_panel")*/), mParcel(parcel) +{ +} + + +// virtual +LLPanelLandAudio::~LLPanelLandAudio() +{ +} + + +BOOL LLPanelLandAudio::postBuild() +{ + mCheckSoundLocal = getChild<LLCheckBoxCtrl>("check sound local"); + childSetCommitCallback("check sound local", onCommitAny, this); + + mRadioVoiceChat = getChild<LLRadioGroup>("parcel_voice_channel"); + childSetCommitCallback("parcel_voice_channel", onCommitAny, this); + + mMusicURLEdit = getChild<LLLineEditor>("music_url"); + childSetCommitCallback("music_url", onCommitAny, this); + + mMusicUrlCheck = getChild<LLCheckBoxCtrl>("hide_music_url"); + childSetCommitCallback("hide_music_url", onCommitAny, this); + + return TRUE; +} + + +// public +void LLPanelLandAudio::refresh() +{ + LLParcel *parcel = mParcel->getParcel(); + + if (!parcel) + { + clearCtrls(); + } + else + { + // something selected, hooray! + + // Display options + BOOL can_change_media = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_MEDIA); + + mCheckSoundLocal->set( parcel->getSoundLocal() ); + mCheckSoundLocal->setEnabled( can_change_media ); + + mMusicUrlCheck->set( parcel->getObscureMusic() ); + mMusicUrlCheck->setEnabled( can_change_media ); + + if(parcel->getParcelFlagAllowVoice()) + { + if(parcel->getParcelFlagUseEstateVoiceChannel()) + mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatEstate); + else + mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatPrivate); + } + else + { + mRadioVoiceChat->setSelectedIndex(kRadioVoiceChatDisable); + } + + mRadioVoiceChat->setEnabled( can_change_media ); + + mMusicURLEdit->setText(parcel->getMusicURL()); + mMusicURLEdit->setEnabled( can_change_media ); + } +} +// static +void LLPanelLandAudio::onCommitAny(LLUICtrl*, void *userdata) +{ + LLPanelLandAudio *self = (LLPanelLandAudio *)userdata; + + LLParcel* parcel = self->mParcel->getParcel(); + if (!parcel) + { + return; + } + + // Extract data from UI + BOOL sound_local = self->mCheckSoundLocal->get(); + int voice_setting = self->mRadioVoiceChat->getSelectedIndex(); + std::string music_url = self->mMusicURLEdit->getText(); + U8 obscure_music = self->mMusicUrlCheck->get(); + + + BOOL voice_enabled; + BOOL voice_estate_chan; + + switch(voice_setting) + { + default: + case kRadioVoiceChatEstate: + voice_enabled = TRUE; + voice_estate_chan = TRUE; + break; + case kRadioVoiceChatPrivate: + voice_enabled = TRUE; + voice_estate_chan = FALSE; + break; + case kRadioVoiceChatDisable: + voice_enabled = FALSE; + voice_estate_chan = FALSE; + break; + } + + // Remove leading/trailing whitespace (common when copying/pasting) + LLStringUtil::trim(music_url); + + // Push data into current parcel + parcel->setParcelFlag(PF_ALLOW_VOICE_CHAT, voice_enabled); + parcel->setParcelFlag(PF_USE_ESTATE_VOICE_CHAN, voice_estate_chan); + parcel->setParcelFlag(PF_SOUND_LOCAL, sound_local); + parcel->setMusicURL(music_url); + parcel->setObscureMusic(obscure_music); + + // Send current parcel data upstream to server + LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel ); + + // Might have changed properties, so let's redraw! + self->refresh(); +} diff --git a/indra/newview/llpanellandaudio.h b/indra/newview/llpanellandaudio.h new file mode 100644 index 0000000000..de5da95fa4 --- /dev/null +++ b/indra/newview/llpanellandaudio.h @@ -0,0 +1,62 @@ +/** + * @file llpanellandaudio.h + * @brief Allows configuration of "audio" for a land parcel. + * + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 LLPANELLANDAUDIO_H +#define LLPANELLANDAUDIO_H + +#include "lllineeditor.h" +#include "llpanel.h" +#include "llparcelselection.h" +#include "lluifwd.h" // widget pointer types + +class LLPanelLandAudio + : public LLPanel +{ +public: + LLPanelLandAudio(LLSafeHandle<LLParcelSelection>& parcelp); + /*virtual*/ ~LLPanelLandAudio(); + /*virtual*/ BOOL postBuild(); + void refresh(); + +private: + static void onCommitAny(LLUICtrl* ctrl, void *userdata); + +private: + LLCheckBoxCtrl* mCheckSoundLocal; + LLRadioGroup* mRadioVoiceChat; + LLLineEditor* mMusicURLEdit; + LLCheckBoxCtrl* mMusicUrlCheck; + + LLSafeHandle<LLParcelSelection>& mParcel; +}; + +#endif diff --git a/indra/newview/llpanellandmedia.cpp b/indra/newview/llpanellandmedia.cpp index d1ab3510cd..994bf7e3f9 100644 --- a/indra/newview/llpanellandmedia.cpp +++ b/indra/newview/llpanellandmedia.cpp @@ -39,6 +39,8 @@ #include "llmimetypes.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" +#include "llviewermedia.h" +#include "llviewerparcelmedia.h" #include "lluictrlfactory.h" // library includes @@ -54,6 +56,7 @@ #include "llsdutil.h" #include "lltexturectrl.h" #include "roles_constants.h" +#include "llscrolllistctrl.h" //--------------------------------------------------------------------------- // LLPanelLandMedia @@ -62,12 +65,6 @@ LLPanelLandMedia::LLPanelLandMedia(LLParcelSelectionHandle& parcel) : LLPanel(), mParcel(parcel), - mCheckSoundLocal(NULL), - mSoundHelpButton(NULL), - mCheckEnableVoiceChat(NULL), - mCheckEnableVoiceChatIsEstateDisabled(NULL), - mCheckEnableVoiceChatParcel(NULL), - mMusicURLEdit(NULL), mMediaURLEdit(NULL), mMediaDescEdit(NULL), mMediaTypeCombo(NULL), @@ -78,8 +75,7 @@ LLPanelLandMedia::LLPanelLandMedia(LLParcelSelectionHandle& parcel) mMediaTextureCtrl(NULL), mMediaAutoScaleCheck(NULL), mMediaLoopCheck(NULL), - mMediaUrlCheck(NULL), - mMusicUrlCheck(NULL) + mMediaUrlCheck(NULL) { } @@ -87,34 +83,10 @@ LLPanelLandMedia::LLPanelLandMedia(LLParcelSelectionHandle& parcel) // virtual LLPanelLandMedia::~LLPanelLandMedia() { - // close LLFloaterURLEntry? } - -// static -void LLPanelLandMedia::onClickSoundHelp(void*) -{ - LLNotifications::instance().add("ClickSoundHelpLand"); -} - - BOOL LLPanelLandMedia::postBuild() { - mCheckSoundLocal = getChild<LLCheckBoxCtrl>("check sound local"); - childSetCommitCallback("check sound local", onCommitAny, this); - - mSoundHelpButton = getChild<LLButton>("?"); - mSoundHelpButton->setClickedCallback(onClickSoundHelp, this); - - mCheckEnableVoiceChat = getChild<LLCheckBoxCtrl>("parcel_enable_voice_channel"); - childSetCommitCallback("parcel_enable_voice_channel", onCommitAny, this); - mCheckEnableVoiceChatIsEstateDisabled = getChild<LLCheckBoxCtrl>("parcel_enable_voice_channel_is_estate_disabled"); - childSetCommitCallback("parcel_enable_voice_channel_is_estate_disabled", onCommitAny, this); - mCheckEnableVoiceChatParcel = getChild<LLCheckBoxCtrl>("parcel_enable_voice_channel_parcel"); - childSetCommitCallback("parcel_enable_voice_channel_parcel", onCommitAny, this); - - mMusicURLEdit = getChild<LLLineEditor>("music_url"); - childSetCommitCallback("music_url", onCommitAny, this); mMediaTextureCtrl = getChild<LLTextureCtrl>("media texture"); mMediaTextureCtrl->setCommitCallback( onCommitAny, this ); @@ -126,16 +98,13 @@ BOOL LLPanelLandMedia::postBuild() childSetCommitCallback("media_auto_scale", onCommitAny, this); mMediaLoopCheck = getChild<LLCheckBoxCtrl>("media_loop"); - childSetCommitCallback("media_loop", onCommitAny, this); + childSetCommitCallback("media_loop", onCommitAny, this ); mMediaUrlCheck = getChild<LLCheckBoxCtrl>("hide_media_url"); - childSetCommitCallback("hide_media_url", onCommitAny, this); - - mMusicUrlCheck = getChild<LLCheckBoxCtrl>("hide_music_url"); - childSetCommitCallback("hide_music_url", onCommitAny, this); + childSetCommitCallback("hide_media_url", onCommitAny, this ); mMediaURLEdit = getChild<LLLineEditor>("media_url"); - childSetCommitCallback("media_url", onCommitAny, this); + childSetCommitCallback("media_url", onCommitAny, this ); mMediaDescEdit = getChild<LLLineEditor>("url_description"); childSetCommitCallback("url_description", onCommitAny, this); @@ -153,6 +122,9 @@ BOOL LLPanelLandMedia::postBuild() mSetURLButton = getChild<LLButton>("set_media_url"); childSetAction("set_media_url", onSetBtn, this); + mResetURLButton = getChild<LLButton>("reset_media_url"); + childSetAction("reset_media_url", onResetBtn, this); + return TRUE; } @@ -173,46 +145,11 @@ void LLPanelLandMedia::refresh() // Display options BOOL can_change_media = LLViewerParcelMgr::isParcelModifiableByAgent(parcel, GP_LAND_CHANGE_MEDIA); - mCheckSoundLocal->set( parcel->getSoundLocal() ); - mCheckSoundLocal->setEnabled( can_change_media ); - - LLViewerRegion* region = LLViewerParcelMgr::getInstance()->getSelectionRegion(); - if (!region) - { - // never seen this happen, but log it - llwarns << "Couldn't get selected region." << llendl; - } - - if (region && region->isVoiceEnabled()) // estate-wide voice-disable overrides all - { - bool allow_voice = parcel->getParcelFlagAllowVoice(); - - mCheckEnableVoiceChatIsEstateDisabled->setVisible(false); - - mCheckEnableVoiceChat->setVisible(true); - mCheckEnableVoiceChat->setEnabled( can_change_media ); - mCheckEnableVoiceChat->set(allow_voice); - - mCheckEnableVoiceChatParcel->setEnabled( can_change_media && allow_voice ); - } - else // disabled at region level - { - mCheckEnableVoiceChatIsEstateDisabled->setVisible(true); // always disabled - mCheckEnableVoiceChat->setVisible(false); - mCheckEnableVoiceChat->setEnabled(false); - mCheckEnableVoiceChat->set(false); - - mCheckEnableVoiceChatParcel->setEnabled(false); - } - - mCheckEnableVoiceChatParcel->set(!parcel->getParcelFlagUseEstateVoiceChannel()); - - mMusicURLEdit->setText(parcel->getMusicURL()); - mMusicURLEdit->setEnabled( can_change_media ); - mMediaURLEdit->setText(parcel->getMediaURL()); mMediaURLEdit->setEnabled( FALSE ); + childSetText("current_url", parcel->getMediaCurrentURL()); + mMediaDescEdit->setText(parcel->getMediaDesc()); mMediaDescEdit->setEnabled( can_change_media ); @@ -228,15 +165,11 @@ void LLPanelLandMedia::refresh() mMediaUrlCheck->set( parcel->getObscureMedia() ); mMediaUrlCheck->setEnabled( can_change_media ); - mMusicUrlCheck->set( parcel->getObscureMusic() ); - mMusicUrlCheck->setEnabled( can_change_media ); - // don't display urls if you're not able to change it // much requested change in forums so people can't 'steal' urls // NOTE: bug#2009 means this is still vunerable - however, bug // should be closed since this bug opens up major security issues elsewhere. bool obscure_media = ! can_change_media && parcel->getObscureMedia(); - bool obscure_music = ! can_change_media && parcel->getObscureMusic(); // Special code to disable asterixes for html type if(mime_type == "text/html") @@ -246,7 +179,6 @@ void LLPanelLandMedia::refresh() mMediaUrlCheck->setEnabled( false ); } - mMusicURLEdit->setDrawAsterixes( obscure_music ); mMediaURLEdit->setDrawAsterixes( obscure_media ); mMediaAutoScaleCheck->set( parcel->getMediaAutoScale () ); @@ -260,7 +192,7 @@ void LLPanelLandMedia::refresh() else mMediaLoopCheck->set( false ); mMediaLoopCheck->setEnabled ( can_change_media && allow_looping ); - + // disallow media size change for mime types that don't allow it bool allow_resize = LLMIMETypes::findAllowResize( mime_type ); if ( allow_resize ) @@ -283,22 +215,7 @@ void LLPanelLandMedia::refresh() mMediaTextureCtrl->setEnabled( can_change_media ); mSetURLButton->setEnabled( can_change_media ); - - #if 0 - // there is a media url and a media texture selected - if ( ( ! ( std::string ( parcel->getMediaURL() ).empty () ) ) && ( ! ( parcel->getMediaID ().isNull () ) ) ) - { - // turn on transport controls if allowed for this parcel - mMediaStopButton->setEnabled ( editable ); - mMediaStartButton->setEnabled ( editable ); - } - else - { - // no media url or no media texture - mMediaStopButton->setEnabled ( FALSE ); - mMediaStartButton->setEnabled ( FALSE ); - }; - #endif + mResetURLButton->setEnabled( can_change_media ); LLFloaterURLEntry* floater_url_entry = (LLFloaterURLEntry*)mURLEntryFloater.get(); if (floater_url_entry) @@ -346,12 +263,19 @@ void LLPanelLandMedia::setMediaType(const std::string& mime_type) void LLPanelLandMedia::setMediaURL(const std::string& media_url) { mMediaURLEdit->setText(media_url); + LLParcel *parcel = mParcel->getParcel(); + if(parcel) + parcel->setMediaCurrentURL(media_url); + // LLViewerMedia::navigateHome(); + + mMediaURLEdit->onCommit(); + // LLViewerParcelMedia::sendMediaNavigateMessage(media_url); + childSetText("current_url", media_url); } - std::string LLPanelLandMedia::getMediaURL() { - return mMediaURLEdit->getText(); + return mMediaURLEdit->getText(); } // static @@ -380,33 +304,23 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl*, void *userdata) } // Extract data from UI - BOOL sound_local = self->mCheckSoundLocal->get(); - std::string music_url = self->mMusicURLEdit->getText(); std::string media_url = self->mMediaURLEdit->getText(); std::string media_desc = self->mMediaDescEdit->getText(); std::string mime_type = self->childGetText("mime_type"); U8 media_auto_scale = self->mMediaAutoScaleCheck->get(); U8 media_loop = self->mMediaLoopCheck->get(); U8 obscure_media = self->mMediaUrlCheck->get(); - U8 obscure_music = self->mMusicUrlCheck->get(); S32 media_width = (S32)self->mMediaWidthCtrl->get(); S32 media_height = (S32)self->mMediaHeightCtrl->get(); LLUUID media_id = self->mMediaTextureCtrl->getImageAssetID(); - BOOL voice_enabled = self->mCheckEnableVoiceChat->get(); - BOOL voice_estate_chan = ! self->mCheckEnableVoiceChatParcel->get(); self->childSetText("mime_type", mime_type); // Remove leading/trailing whitespace (common when copying/pasting) - LLStringUtil::trim(music_url); LLStringUtil::trim(media_url); // Push data into current parcel - parcel->setParcelFlag(PF_ALLOW_VOICE_CHAT, voice_enabled); - parcel->setParcelFlag(PF_USE_ESTATE_VOICE_CHAN, voice_estate_chan); - parcel->setParcelFlag(PF_SOUND_LOCAL, sound_local); - parcel->setMusicURL(music_url); parcel->setMediaURL(media_url); parcel->setMediaType(mime_type); parcel->setMediaDesc(media_desc); @@ -416,7 +330,6 @@ void LLPanelLandMedia::onCommitAny(LLUICtrl*, void *userdata) parcel->setMediaAutoScale ( media_auto_scale ); parcel->setMediaLoop ( media_loop ); parcel->setObscureMedia( obscure_media ); - parcel->setObscureMusic( obscure_music ); // Send current parcel data upstream to server LLViewerParcelMgr::getInstance()->sendParcelPropertiesUpdate( parcel ); @@ -435,3 +348,16 @@ void LLPanelLandMedia::onSetBtn(void *userdata) parent_floater->addDependentFloater(self->mURLEntryFloater.get()); } } + +// static +void LLPanelLandMedia::onResetBtn(void *userdata) +{ + LLPanelLandMedia *self = (LLPanelLandMedia *)userdata; + LLParcel* parcel = self->mParcel->getParcel(); + // LLViewerMedia::navigateHome(); + self->refresh(); + self->childSetText("current_url", parcel->getMediaURL()); + // LLViewerParcelMedia::sendMediaNavigateMessage(parcel->getMediaURL()); + +} + diff --git a/indra/newview/llpanellandmedia.h b/indra/newview/llpanellandmedia.h index 6a53dd42a5..5ad1f9758d 100644 --- a/indra/newview/llpanellandmedia.h +++ b/indra/newview/llpanellandmedia.h @@ -56,29 +56,34 @@ private: static void onCommitAny(LLUICtrl* ctrl, void *userdata); static void onCommitType(LLUICtrl* ctrl, void *userdata); static void onSetBtn(void* userdata); - static void onClickSoundHelp(void*); - + static void onResetBtn(void* userdata); + private: - LLCheckBoxCtrl* mCheckSoundLocal; - LLButton* mSoundHelpButton; - LLCheckBoxCtrl* mCheckEnableVoiceChat; - LLCheckBoxCtrl* mCheckEnableVoiceChatIsEstateDisabled; - LLCheckBoxCtrl* mCheckEnableVoiceChatParcel; - LLLineEditor* mMusicURLEdit; LLLineEditor* mMediaURLEdit; LLLineEditor* mMediaDescEdit; LLComboBox* mMediaTypeCombo; LLButton* mSetURLButton; + LLButton* mResetURLButton; + LLSpinCtrl* mMediaResetCtrl; LLSpinCtrl* mMediaHeightCtrl; LLSpinCtrl* mMediaWidthCtrl; + LLTextBox* mMediaResetCtrlLabel; LLTextBox* mMediaSizeCtrlLabel; LLTextureCtrl* mMediaTextureCtrl; LLCheckBoxCtrl* mMediaAutoScaleCheck; LLCheckBoxCtrl* mMediaLoopCheck; LLCheckBoxCtrl* mMediaUrlCheck; - LLCheckBoxCtrl* mMusicUrlCheck; LLHandle<LLFloater> mURLEntryFloater; + LLCheckBoxCtrl* mMediaNavigateAllowCheck; + LLCheckBoxCtrl* mMediaURLFilterCheck; + LLLineEditor* mMediaURLFilterDomainEdit; + LLButton* mMediaURLFilterAddButton; + LLButton* mMediaURLFilterRemoveButton; + LLScrollListCtrl* mURLFilterList; + LLRadioGroup* mRadioNavigateControl; + + LLSafeHandle<LLParcelSelection>& mParcel; }; diff --git a/indra/newview/llpanellogin.cpp b/indra/newview/llpanellogin.cpp index 7007bfc9d5..167446d380 100644 --- a/indra/newview/llpanellogin.cpp +++ b/indra/newview/llpanellogin.cpp @@ -66,11 +66,10 @@ #include "lluictrlfactory.h" #include "llhttpclient.h" #include "llweb.h" -#include "llwebbrowserctrl.h" -#include "llfloaterhtml.h" +#include "llmediactrl.h" #include "llrootview.h" -#include "llfloaterhtmlhelp.h" +#include "llfloatermediabrowser.h" #include "llfloatertos.h" #include "lltrans.h" #include "llglheaders.h" @@ -89,7 +88,7 @@ class LLLoginRefreshHandler : public LLCommandHandler public: // don't allow from external browsers LLLoginRefreshHandler() : LLCommandHandler("login_refresh", true) { } - bool handle(const LLSD& tokens, const LLSD& query_map, LLWebBrowserCtrl* web) + bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (LLStartUp::getStartupState() < STATE_LOGIN_CLEANUP) { @@ -257,16 +256,15 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, #endif // get the web browser control - LLWebBrowserCtrl* web_browser = getChild<LLWebBrowserCtrl>("login_html"); + LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("login_html"); + web_browser->addObserver(this); + // Need to handle login secondlife:///app/ URLs web_browser->setTrusted( true ); - // observe browser events - web_browser->addObserver( this ); - // don't make it a tab stop until SL-27594 is fixed web_browser->setTabStop(FALSE); - web_browser->navigateToLocalPage( "loading", "loading.html" ); + // web_browser->navigateToLocalPage( "loading", "loading.html" ); // make links open in external browser web_browser->setOpenInExternalBrowser( true ); @@ -300,7 +298,7 @@ LLPanelLogin::LLPanelLogin(const LLRect &rect, void LLPanelLogin::setSiteIsAlive( bool alive ) { - LLWebBrowserCtrl* web_browser = getChild<LLWebBrowserCtrl>("login_html"); + LLMediaCtrl* web_browser = getChild<LLMediaCtrl>("login_html"); // if the contents of the site was retrieved if ( alive ) { @@ -680,7 +678,7 @@ void LLPanelLogin::setAlwaysRefresh(bool refresh) { if (LLStartUp::getStartupState() >= STATE_LOGIN_CLEANUP) return; - LLWebBrowserCtrl* web_browser = sInstance->getChild<LLWebBrowserCtrl>("login_html"); + LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html"); if (web_browser) { @@ -820,25 +818,28 @@ void LLPanelLogin::loadLoginPage() #endif #endif - LLWebBrowserCtrl* web_browser = sInstance->getChild<LLWebBrowserCtrl>("login_html"); + LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html"); // navigate to the "real" page - web_browser->navigateTo( oStr.str() ); + web_browser->navigateTo( oStr.str(), "text/html" ); } -void LLPanelLogin::onNavigateComplete( const EventType& eventIn ) +void LLPanelLogin::handleMediaEvent(LLPluginClassMedia* /*self*/, EMediaEvent event) { - LLWebBrowserCtrl* web_browser = sInstance->getChild<LLWebBrowserCtrl>("login_html"); - if (web_browser) + if(event == MEDIA_EVENT_NAVIGATE_COMPLETE) { - // *HACK HACK HACK HACK! - /* Stuff a Tab key into the browser now so that the first field will - ** get the focus! The embedded javascript on the page that properly - ** sets the initial focus in a real web browser is not working inside - ** the viewer, so this is an UGLY HACK WORKAROUND for now. - */ - // Commented out as it's not reliable - //web_browser->handleKey(KEY_TAB, MASK_NONE, false); + LLMediaCtrl* web_browser = sInstance->getChild<LLMediaCtrl>("login_html"); + if (web_browser) + { + // *HACK HACK HACK HACK! + /* Stuff a Tab key into the browser now so that the first field will + ** get the focus! The embedded javascript on the page that properly + ** sets the initial focus in a real web browser is not working inside + ** the viewer, so this is an UGLY HACK WORKAROUND for now. + */ + // Commented out as it's not reliable + //web_browser->handleKey(KEY_TAB, MASK_NONE, false); + } } } diff --git a/indra/newview/llpanellogin.h b/indra/newview/llpanellogin.h index 83a31eecda..ffcf6a9b70 100644 --- a/indra/newview/llpanellogin.h +++ b/indra/newview/llpanellogin.h @@ -35,7 +35,7 @@ #include "llpanel.h" #include "llpointer.h" // LLPointer<> -#include "llwebbrowserctrl.h" // LLWebBrowserCtrlObserver +#include "llmediactrl.h" // LLMediaCtrlObserver class LLLineEditor; class LLUIImage; @@ -43,7 +43,7 @@ class LLUIImage; class LLPanelLogin: public LLPanel, - public LLWebBrowserCtrlObserver + public LLViewerMediaObserver { LOG_CLASS(LLPanelLogin); public: @@ -82,13 +82,15 @@ public: static void setAlwaysRefresh(bool refresh); static void mungePassword(LLUICtrl* caller, void* user_data); + // inherited from LLViewerMediaObserver + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + private: static void onClickConnect(void*); static void onClickNewAccount(void*); // static bool newAccountAlertCallback(const LLSD& notification, const LLSD& response); static void onClickQuit(void*); static void onClickVersion(void*); - virtual void onNavigateComplete( const EventType& eventIn ); static void onClickForgotPassword(void*); static void onPassKey(LLLineEditor* caller, void* user_data); static void onSelectServer(LLUICtrl*, void*); diff --git a/indra/newview/llpanelpeople.cpp b/indra/newview/llpanelpeople.cpp index a8e3fd3195..698f442d7c 100644 --- a/indra/newview/llpanelpeople.cpp +++ b/indra/newview/llpanelpeople.cpp @@ -92,7 +92,10 @@ public: * * This may start repeated updates until all names are complete. */ - virtual void forceUpdate() {} + virtual void forceUpdate() + { + updateList(); + } /** * Activate/deactivate updater. @@ -209,11 +212,6 @@ public: } } - /*virtual*/ void forceUpdate() - { - updateList(); - } - /*virtual*/ BOOL tick() { updateList(); @@ -283,11 +281,6 @@ public: gAgent.removeListener(this); } - /*virtual*/ void forceUpdate() - { - updateList(); - } - /*virtual*/ bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) { // Why is "new group" sufficient? @@ -308,7 +301,8 @@ LLPanelPeople::LLPanelPeople() mFilterSubString(LLStringUtil::null), mFilterEditor(NULL), mTabContainer(NULL), - mFriendList(NULL), + mOnlineFriendList(NULL), + mOfflineFriendList(NULL), mNearbyList(NULL), mRecentList(NULL) { @@ -326,6 +320,10 @@ LLPanelPeople::~LLPanelPeople() delete mGroupListUpdater; LLView::deleteViewByHandle(mGroupPlusMenuHandle); + LLView::deleteViewByHandle(mNearbyViewSortMenuHandle); + LLView::deleteViewByHandle(mFriendsViewSortMenuHandle); + LLView::deleteViewByHandle(mRecentViewSortMenuHandle); + } BOOL LLPanelPeople::postBuild() @@ -337,10 +335,12 @@ BOOL LLPanelPeople::postBuild() mTabContainer = getChild<LLTabContainer>("tabs"); mTabContainer->setCommitCallback(boost::bind(&LLPanelPeople::onTabSelected, this, _2)); - mTabContainer->selectTabByName(FRIENDS_TAB_NAME); // must go after setting commit callback - mFriendList = getChild<LLPanel>(FRIENDS_TAB_NAME)->getChild<LLAvatarList>("avatar_list"); + mOnlineFriendList = getChild<LLPanel>(FRIENDS_TAB_NAME)->getChild<LLAvatarList>("avatars_online"); + mOfflineFriendList = getChild<LLPanel>(FRIENDS_TAB_NAME)->getChild<LLAvatarList>("avatars_offline"); + mNearbyList = getChild<LLPanel>(NEARBY_TAB_NAME)->getChild<LLAvatarList>("avatar_list"); + mRecentList = getChild<LLPanel>(RECENT_TAB_NAME)->getChild<LLAvatarList>("avatar_list"); mGroupList = getChild<LLGroupList>("group_list"); @@ -353,10 +353,12 @@ BOOL LLPanelPeople::postBuild() friends_panel->childSetAction("add_btn", boost::bind(&LLPanelPeople::onAddFriendWizButtonClicked, this)); friends_panel->childSetAction("del_btn", boost::bind(&LLPanelPeople::onDeleteFriendButtonClicked, this)); - mFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mFriendList)); + mOnlineFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mOnlineFriendList)); + mOfflineFriendList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mOfflineFriendList)); mNearbyList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mNearbyList)); mRecentList->setDoubleClickCallback(boost::bind(&LLPanelPeople::onAvatarListDoubleClicked, this, mRecentList)); - mFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mFriendList)); + mOnlineFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mOnlineFriendList)); + mOfflineFriendList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mOfflineFriendList)); mNearbyList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mNearbyList)); mRecentList->setCommitCallback(boost::bind(&LLPanelPeople::onAvatarListCommitted, this, mRecentList)); @@ -371,18 +373,44 @@ BOOL LLPanelPeople::postBuild() buttonSetAction("call_btn", boost::bind(&LLPanelPeople::onCallButtonClicked, this)); buttonSetAction("teleport_btn", boost::bind(&LLPanelPeople::onTeleportButtonClicked, this)); buttonSetAction("share_btn", boost::bind(&LLPanelPeople::onShareButtonClicked, this)); - buttonSetAction("more_btn", boost::bind(&LLPanelPeople::onMoreButtonClicked, this)); + + getChild<LLPanel>(NEARBY_TAB_NAME)->childSetAction("nearby_view_sort_btn",boost::bind(&LLPanelPeople::onNearbyViewSortButtonClicked, this)); + getChild<LLPanel>(RECENT_TAB_NAME)->childSetAction("recent_viewsort_btn",boost::bind(&LLPanelPeople::onRecentViewSortButtonClicked, this)); + getChild<LLPanel>(FRIENDS_TAB_NAME)->childSetAction("friends_viewsort_btn",boost::bind(&LLPanelPeople::onFriendsViewSortButtonClicked, this)); + + // Must go after setting commit callback and initializing all pointers to children. + mTabContainer->selectTabByName(FRIENDS_TAB_NAME); // Create menus. LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("People.Group.Plus.Action", boost::bind(&LLPanelPeople::onGroupPlusMenuItemClicked, this, _2)); + registrar.add("People.Friends.ViewSort.Action", boost::bind(&LLPanelPeople::onFriendsViewSortMenuItemClicked, this, _2)); + registrar.add("People.Nearby.ViewSort.Action", boost::bind(&LLPanelPeople::onNearbyViewSortMenuItemClicked, this, _2)); + registrar.add("People.Recent.ViewSort.Action", boost::bind(&LLPanelPeople::onRecentViewSortMenuItemClicked, this, _2)); + LLMenuGL* plus_menu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_group_plus.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); mGroupPlusMenuHandle = plus_menu->getHandle(); + LLMenuGL* nearby_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_nearby_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(nearby_view_sort) + mNearbyViewSortMenuHandle = nearby_view_sort->getHandle(); + + LLMenuGL* friend_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_friends_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(friend_view_sort) + mFriendsViewSortMenuHandle = friend_view_sort->getHandle(); + + LLMenuGL* recent_view_sort = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_people_recent_view_sort.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if(recent_view_sort) + mRecentViewSortMenuHandle = recent_view_sort->getHandle(); + + + // Perform initial update. mFriendListUpdater->forceUpdate(); - updateGroupList(); - updateRecentList(); + mRecentListUpdater->forceUpdate(); + mGroupListUpdater->forceUpdate(); + mRecentListUpdater->forceUpdate(); return TRUE; } @@ -393,16 +421,24 @@ bool LLPanelPeople::updateFriendList(U32 changed_mask) if (changed_mask & (LLFriendObserver::ADD | LLFriendObserver::REMOVE | LLFriendObserver::ONLINE)) { // get all buddies we know about + const LLAvatarTracker& av_tracker = LLAvatarTracker::instance(); LLAvatarTracker::buddy_map_t all_buddies; - LLAvatarTracker::instance().copyBuddyList(all_buddies); + av_tracker.copyBuddyList(all_buddies); - // *TODO: it's suboptimal to rebuild the whole list on online status change. + // *TODO: it's suboptimal to rebuild the whole lists on online status change. - // convert the buddy map to vector - mFriendVec.clear(); + // save them to the online and offline friends vectors + mOnlineFriendVec.clear(); + mOfflineFriendVec.clear(); LLAvatarTracker::buddy_map_t::const_iterator buddy_it = all_buddies.begin(); for (; buddy_it != all_buddies.end(); ++buddy_it) - mFriendVec.push_back(buddy_it->first); + { + LLUUID buddy_id = buddy_it->first; + if (av_tracker.isBuddyOnline(buddy_id)) + mOnlineFriendVec.push_back(buddy_id); + else + mOfflineFriendVec.push_back(buddy_id); + } return filterFriendList(); } @@ -428,6 +464,9 @@ bool LLPanelPeople::updateRecentList() bool LLPanelPeople::updateGroupList() { + if (!mGroupList) + return true; // there's no point in further updates + bool have_names = mGroupList->update(mFilterSubString); if (mGroupList->isEmpty()) @@ -438,11 +477,19 @@ bool LLPanelPeople::updateGroupList() bool LLPanelPeople::filterFriendList() { + if (!mOnlineFriendList || !mOfflineFriendList) + return true; // there's no point in further updates + // We must always update Friends list to clear the latest removed friend. - bool have_names = mFriendList->update(mFriendVec, mFilterSubString); + bool have_names = + mOnlineFriendList->update(mOnlineFriendVec, mFilterSubString) & + mOfflineFriendList->update(mOfflineFriendVec, mFilterSubString); - if (mFriendVec.size() == 0) - mFriendList->setCommentText(getString("no_friends")); + if (mOnlineFriendVec.size() == 0) + mOnlineFriendList->setCommentText(getString("no_friends_online")); + + if (mOfflineFriendVec.size() == 0) + mOfflineFriendList->setCommentText(getString("no_friends_offline")); return have_names; } @@ -459,6 +506,9 @@ bool LLPanelPeople::filterNearbyList() bool LLPanelPeople::filterRecentList() { + if (!mRecentList) + return true; + if (mRecentVec.size() > 0) return mRecentList->update(mRecentVec, mFilterSubString); @@ -495,7 +545,7 @@ void LLPanelPeople::buttonSetAction(const std::string& btn_name, const commit_si void LLPanelPeople::updateButtons() { - std::string cur_tab = mTabContainer->getCurrentPanel()->getName(); + std::string cur_tab = getActiveTabName(); bool nearby_tab_active = (cur_tab == NEARBY_TAB_NAME); bool friends_tab_active = (cur_tab == FRIENDS_TAB_NAME); bool group_tab_active = (cur_tab == GROUP_TAB_NAME); @@ -507,7 +557,7 @@ void LLPanelPeople::updateButtons() buttonSetVisible("add_friend_btn", nearby_tab_active || recent_tab_active); buttonSetVisible("view_profile_btn", !group_tab_active); buttonSetVisible("im_btn", !group_tab_active); - buttonSetVisible("teleport_btn", friends_tab_active || group_tab_active); + buttonSetVisible("teleport_btn", friends_tab_active); buttonSetVisible("share_btn", !recent_tab_active && false); // not implemented yet if (group_tab_active) @@ -529,10 +579,9 @@ void LLPanelPeople::updateButtons() else { bool is_friend = true; - LLAvatarList* list; // Check whether selected avatar is our friend. - if ((list = getActiveAvatarList()) && (selected_id = list->getCurrentID()).notNull()) + if ((selected_id = getCurrentItemID()).notNull()) { is_friend = LLAvatarTracker::instance().getBuddyInfo(selected_id) != NULL; } @@ -541,7 +590,7 @@ void LLPanelPeople::updateButtons() } bool item_selected = selected_id.notNull(); - buttonSetEnabled("teleport_btn", (friends_tab_active || group_tab_active) && item_selected); + buttonSetEnabled("teleport_btn", friends_tab_active && item_selected); buttonSetEnabled("view_profile_btn", item_selected); buttonSetEnabled("im_btn", item_selected); buttonSetEnabled("call_btn", item_selected && false); // not implemented yet @@ -550,26 +599,36 @@ void LLPanelPeople::updateButtons() buttonSetEnabled("chat_btn", item_selected); } -LLAvatarList* LLPanelPeople::getActiveAvatarList() const +const std::string& LLPanelPeople::getActiveTabName() const +{ + return mTabContainer->getCurrentPanel()->getName(); +} + +LLUUID LLPanelPeople::getCurrentItemID() const { - std::string cur_tab = mTabContainer->getCurrentPanel()->getName(); + std::string cur_tab = getActiveTabName(); + + if (cur_tab == FRIENDS_TAB_NAME) // this tab has two lists + { + LLUUID cur_online_friend; + + if ((cur_online_friend = mOnlineFriendList->getCurrentID()).notNull()) + return cur_online_friend; + + return mOfflineFriendList->getCurrentID(); + } - if (cur_tab == FRIENDS_TAB_NAME) - return mFriendList; if (cur_tab == NEARBY_TAB_NAME) - return mNearbyList; + return mNearbyList->getCurrentID(); + if (cur_tab == RECENT_TAB_NAME) - return mRecentList; + return mRecentList->getCurrentID(); - return NULL; -} + if (cur_tab == GROUP_TAB_NAME) + return mGroupList->getCurrentID(); -LLUUID LLPanelPeople::getCurrentItemID() const -{ - LLAvatarList* alist = getActiveAvatarList(); - if (alist) - return alist->getCurrentID(); - return mGroupList->getCurrentID(); + llassert(0 && "unknown tab selected"); + return LLUUID::null; } void LLPanelPeople::showGroupMenu(LLMenuGL* menu) @@ -651,15 +710,27 @@ void LLPanelPeople::onAvatarListDoubleClicked(LLAvatarList* list) if (clicked_id.isNull()) return; - - // Open mini-inspector for the avatar being clicked - LLFloaterReg::showInstance("mini_inspector", clicked_id); - // inspector will delete itself on close + +#if 0 // SJB: Useful for testing, but not currently functional or to spec + LLAvatarActions::showProfile(clicked_id); +#else // spec says open IM window + LLAvatarActions::startIM(clicked_id); +#endif } void LLPanelPeople::onAvatarListCommitted(LLAvatarList* list) { - (void) list; + // Make sure only one of the friends lists (online/offline) has selection. + if (getActiveTabName() == FRIENDS_TAB_NAME) + { + if (list == mOnlineFriendList) + mOfflineFriendList->deselectAllItems(TRUE); + else if (list == mOfflineFriendList) + mOnlineFriendList->deselectAllItems(TRUE); + else + llassert(0 && "commit on unknown friends list"); + } + updateButtons(); } @@ -767,23 +838,64 @@ void LLPanelPeople::onGroupPlusMenuItemClicked(const LLSD& userdata) LLGroupActions::createGroup(); } -void LLPanelPeople::onCallButtonClicked() +void LLPanelPeople::onFriendsViewSortMenuItemClicked(const LLSD& userdata) { - // *TODO: not implemented yet + std::string chosen_item = userdata.asString(); + + if (chosen_item == "sort_name") + { + } + else if (chosen_item == "sort_status") + { + } + else if (chosen_item == "view_icons") + { + } + else if (chosen_item == "organize_offline") + { + } } +void LLPanelPeople::onNearbyViewSortMenuItemClicked(const LLSD& userdata) +{ + std::string chosen_item = userdata.asString(); -void LLPanelPeople::onTeleportButtonClicked() + if (chosen_item == "sort_recent") + { + } + else if (chosen_item == "sort_name") + { + } + else if (chosen_item == "view_icons") + { + } + else if (chosen_item == "sort_distance") + { + } +} +void LLPanelPeople::onRecentViewSortMenuItemClicked(const LLSD& userdata) { - std::string cur_tab = mTabContainer->getCurrentPanel()->getName(); + std::string chosen_item = userdata.asString(); - if (cur_tab == FRIENDS_TAB_NAME) + if (chosen_item == "sort_most") { - LLAvatarActions::offerTeleport(getCurrentItemID()); } - else if (cur_tab == GROUP_TAB_NAME) + else if (chosen_item == "sort_name") { - LLGroupActions::offerTeleport(getCurrentItemID()); } + else if (chosen_item == "view_icons") + { + } +} + + +void LLPanelPeople::onCallButtonClicked() +{ + // *TODO: not implemented yet +} + +void LLPanelPeople::onTeleportButtonClicked() +{ + LLAvatarActions::offerTeleport(getCurrentItemID()); } void LLPanelPeople::onShareButtonClicked() @@ -795,6 +907,27 @@ void LLPanelPeople::onMoreButtonClicked() { // *TODO: not implemented yet } +void LLPanelPeople::onFriendsViewSortButtonClicked() +{ + LLMenuGL* menu = (LLMenuGL*)mFriendsViewSortMenuHandle.get(); + if (!menu) + return; + showGroupMenu(menu); +} +void LLPanelPeople::onRecentViewSortButtonClicked() +{ + LLMenuGL* menu = (LLMenuGL*)mRecentViewSortMenuHandle.get(); + if (!menu) + return; + showGroupMenu(menu); +} +void LLPanelPeople::onNearbyViewSortButtonClicked() +{ + LLMenuGL* menu = (LLMenuGL*)mNearbyViewSortMenuHandle.get(); + if (!menu) + return; + showGroupMenu(menu); +} void LLPanelPeople::onOpen(const LLSD& key) { diff --git a/indra/newview/llpanelpeople.h b/indra/newview/llpanelpeople.h index 58ed77f0f2..d0f78f4247 100644 --- a/indra/newview/llpanelpeople.h +++ b/indra/newview/llpanelpeople.h @@ -57,15 +57,17 @@ public: class Updater; private: + // methods indirectly called by the updaters bool updateFriendList(U32 changed_mask); bool updateNearbyList(); bool updateRecentList(); bool updateGroupList(); + bool filterFriendList(); bool filterNearbyList(); bool filterRecentList(); void updateButtons(); - LLAvatarList* getActiveAvatarList() const; + const std::string& getActiveTabName() const; LLUUID getCurrentItemID() const; void buttonSetVisible(std::string btn_name, BOOL visible); void buttonSetEnabled(const std::string& btn_name, bool enabled); @@ -91,12 +93,19 @@ private: void onShareButtonClicked(); void onMoreButtonClicked(); void onActivateButtonClicked(); + void onRecentViewSortButtonClicked(); + void onNearbyViewSortButtonClicked(); + void onFriendsViewSortButtonClicked(); void onAvatarListDoubleClicked(LLAvatarList* list); void onAvatarListCommitted(LLAvatarList* list); void onGroupPlusButtonClicked(); void onGroupMinusButtonClicked(); void onGroupPlusMenuItemClicked(const LLSD& userdata); + void onFriendsViewSortMenuItemClicked(const LLSD& userdata); + void onNearbyViewSortMenuItemClicked(const LLSD& userdata); + void onRecentViewSortMenuItemClicked(const LLSD& userdata); + // misc callbacks bool onFriendListUpdate(U32 changed_mask); static void onAvatarPicked( @@ -106,12 +115,16 @@ private: LLFilterEditor* mFilterEditor; LLTabContainer* mTabContainer; - LLAvatarList* mFriendList; + LLAvatarList* mOnlineFriendList; + LLAvatarList* mOfflineFriendList; LLAvatarList* mNearbyList; LLAvatarList* mRecentList; LLGroupList* mGroupList; LLHandle<LLView> mGroupPlusMenuHandle; + LLHandle<LLView> mNearbyViewSortMenuHandle; + LLHandle<LLView> mFriendsViewSortMenuHandle; + LLHandle<LLView> mRecentViewSortMenuHandle; Updater* mFriendListUpdater; Updater* mNearbyListUpdater; @@ -128,7 +141,8 @@ private: // since re-fetching the groups list is always fast. typedef std::vector<LLUUID> uuid_vector_t; uuid_vector_t mNearbyVec; - uuid_vector_t mFriendVec; + uuid_vector_t mOnlineFriendVec; + uuid_vector_t mOfflineFriendVec; uuid_vector_t mRecentVec; }; diff --git a/indra/newview/llpanelpermissions.cpp b/indra/newview/llpanelpermissions.cpp index d515b03ea9..42f9906409 100644 --- a/indra/newview/llpanelpermissions.cpp +++ b/indra/newview/llpanelpermissions.cpp @@ -244,7 +244,7 @@ void LLPanelPermissions::refresh() BOOL is_perm_modify = (LLSelectMgr::getInstance()->getSelection()->getFirstRootNode() && LLSelectMgr::getInstance()->selectGetRootsModify()) || LLSelectMgr::getInstance()->selectGetModify(); - const LLView* keyboard_focus_view = gFocusMgr.getKeyboardFocus(); + const LLFocusableElement* keyboard_focus_view = gFocusMgr.getKeyboardFocus(); S32 string_index = 0; std::string MODIFY_INFO_STRINGS[] = { diff --git a/indra/newview/llpanelpick.cpp b/indra/newview/llpanelpick.cpp index cda1a9e7e7..f7ca54c732 100644 --- a/indra/newview/llpanelpick.cpp +++ b/indra/newview/llpanelpick.cpp @@ -66,7 +66,7 @@ LLPanelPick::LLPanelPick(BOOL edit_mode/* = FALSE */) -: LLPanel(), LLAvatarPropertiesObserver(), +: LLPanel(), LLAvatarPropertiesObserver(), LLRemoteParcelInfoObserver(), mEditMode(edit_mode), mSnapshotCtrl(NULL), mPickId(LLUUID::null), @@ -111,6 +111,8 @@ void LLPanelPick::reset() mDataReceived = FALSE; mPosGlobal.clearVec(); + + childSetValue("maturity", ""); } BOOL LLPanelPick::postBuild() @@ -211,6 +213,12 @@ void LLPanelPick::createNewPick() init(pick_data); mDataReceived = TRUE; LLAvatarPropertiesProcessor::instance().removeObserver(mCreatorId, this); + + if (!mEditMode) + { + LLRemoteParcelInfoProcessor::getInstance()->addObserver(pick_data->parcel_id, this); + LLRemoteParcelInfoProcessor::getInstance()->sendParcelInfoRequest(pick_data->parcel_id); + } } @@ -450,3 +458,24 @@ void LLPanelPick::showOnMap(const LLVector3d& position) LLFloaterWorldMap::getInstance()->trackLocation(position); LLFloaterReg::showInstance("world_map", "center"); } + +void LLPanelPick::processParcelInfo(const LLParcelData& parcel_data) +{ + if (mEditMode) return; + + // HACK: Flag 0x2 == adult region, + // Flag 0x1 == mature region, otherwise assume PG + std::string rating_icon = "icon_event.tga"; + if (parcel_data.flags & 0x2) + { + rating_icon = "icon_event_adult.tga"; + } + else if (parcel_data.flags & 0x1) + { + rating_icon = "icon_event_mature.tga"; + } + + childSetValue("maturity", rating_icon); + + //*NOTE we don't removeObserver(...) ourselves cause LLRemoveParcelProcessor does it for us +} diff --git a/indra/newview/llpanelpick.h b/indra/newview/llpanelpick.h index 15b0d6c541..2cd4706dfe 100644 --- a/indra/newview/llpanelpick.h +++ b/indra/newview/llpanelpick.h @@ -38,12 +38,13 @@ #define LL_LLPANELPICK_H #include "llpanel.h" +#include "llremoteparcelrequest.h" class LLTextureCtrl; class LLMessageSystem; class LLAvatarPropertiesObserver; -class LLPanelPick : public LLPanel, public LLAvatarPropertiesObserver +class LLPanelPick : public LLPanel, public LLAvatarPropertiesObserver, LLRemoteParcelInfoObserver { LOG_CLASS(LLPanelPick); public: @@ -79,6 +80,10 @@ public: static void teleport(const LLVector3d& position); static void showOnMap(const LLVector3d& position); + //This stuff we got from LLRemoteParcelObserver, in the last two we intentionally do nothing + /*virtual*/ void processParcelInfo(const LLParcelData& parcel_data); + /*virtual*/ void setParcelID(const LLUUID& parcel_id) {}; + /*virtual*/ void setErrorStatus(U32 status, const std::string& reason) {}; protected: diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp index afcd8c735c..973afae73b 100644 --- a/indra/newview/llpanelpicks.cpp +++ b/indra/newview/llpanelpicks.cpp @@ -191,24 +191,24 @@ void LLPanelPicks::reshapePicksList() if (!mPickItemList.size()) return; LLView* pickList = getPicksList(); + //We don't need to update size of the 'pick list' before reshaping pick items. Don't need to reshape the pick list + S32 height = mPickItemList.size() * (mPickItemList.front()->getRect().getHeight() + PICK_ITEMS_BETWEEN); + LLRect rc = pickList->getRect(); + rc.setLeftTopAndSize(rc.mLeft, rc.mTop, rc.getWidth(), height); + pickList->setRect(rc); + S32 last_bottom = pickList->getRect().getHeight(); - child_list_const_iter_t child_it, child_first_it = pickList->getChildList()->begin(); - for ( child_it = child_first_it; child_it != pickList->getChildList()->end(); ++child_it) + std::list<LLPickItem*>::const_iterator pick_it, pick_first_it = mPickItemList.begin(); + for ( pick_it = pick_first_it; pick_it != mPickItemList.end(); ++pick_it) { - LLView* const childp = *child_it; - if(child_it != child_first_it) + LLView* const pick = *pick_it; + if(pick_it != pick_first_it) { - last_bottom -= childp->getRect().getHeight(); + last_bottom -= pick->getRect().getHeight(); last_bottom -= PICK_ITEMS_BETWEEN; } - reshapePickItem(childp, last_bottom,pickList->getRect().getWidth()); + reshapePickItem(pick, last_bottom,pickList->getRect().getWidth()); } - - S32 height = pickList->getChildCount() * ((*child_first_it)->getRect().getHeight() + PICK_ITEMS_BETWEEN); - LLRect rc = pickList->getRect(); - rc.setLeftTopAndSize(rc.mLeft, rc.mTop, rc.getWidth(), height); - pickList->reshape(rc.getWidth(), rc.getHeight()); - pickList->setRect(rc); } void LLPanelPicks::reshapePickItem(LLView* const pick_item, const S32 last_bottom, const S32 newWidth) @@ -593,3 +593,11 @@ void LLPickItem::processProperties(void *data, EAvatarProcessorType type) LLAvatarPropertiesProcessor::instance().removeObserver(mCreatorID, this); } +void LLPanelPicks::onClose() +{ + // Toggle off Pick Info panel if it is visible. + if(mPickPanel && mPickPanel->getVisible()) + { + getProfilePanel()->togglePanel(mPickPanel); + } +} diff --git a/indra/newview/llpanelpicks.h b/indra/newview/llpanelpicks.h index e0e7f69532..da7bc32ab1 100644 --- a/indra/newview/llpanelpicks.h +++ b/indra/newview/llpanelpicks.h @@ -83,6 +83,11 @@ public: // parent panels failed to work (picks related code was in me profile panel) void setProfilePanel(LLPanelProfile* profile_panel); + /** + * Closes LLPanelPick if it is visible. + */ + /*virtual*/ void onClose(); + private: void onClickDelete(); void onClickTeleport(); diff --git a/indra/newview/llpanelplace.cpp b/indra/newview/llpanelplace.cpp index 83702c08ab..61e18195b8 100644 --- a/indra/newview/llpanelplace.cpp +++ b/indra/newview/llpanelplace.cpp @@ -48,7 +48,7 @@ #include "lllineeditor.h" #include "lluiconstants.h" #include "lltextbox.h" -#include "llviewertexteditor.h" +#include "lltexteditor.h" #include "lltexturectrl.h" #include "lltrans.h" #include "llworldmap.h" diff --git a/indra/newview/llpanelplaceinfo.cpp b/indra/newview/llpanelplaceinfo.cpp index d6d827356b..3926ee0f07 100644 --- a/indra/newview/llpanelplaceinfo.cpp +++ b/indra/newview/llpanelplaceinfo.cpp @@ -49,6 +49,7 @@ #include "lltextbox.h" #include "llagent.h" +#include "llavatarpropertiesprocessor.h" #include "llfloaterworldmap.h" #include "llinventorymodel.h" #include "lllandmarkactions.h" @@ -98,6 +99,10 @@ BOOL LLPanelPlaceInfo::postBuild() mDescEditor = getChild<LLTextEditor>("description"); mRating = getChild<LLIconCtrl>("maturity"); + mRegionInfoDrillIn = getChild<LLButton>("region_info_drill_in"); + mMediaDrillIn = getChild<LLButton>("media_drill_in"); + mMediaDrillIn->setClickedCallback(boost::bind(&LLPanelPlaceInfo::toggleMediaPanel, this, TRUE)); + mOwner = getChild<LLTextBox>("owner"); mCreator = getChild<LLTextBox>("creator"); mCreated = getChild<LLTextBox>("created"); @@ -242,15 +247,17 @@ void LLPanelPlaceInfo::setParcelID(const LLUUID& parcel_id) void LLPanelPlaceInfo::setInfoType(INFO_TYPE type) { - if (type != PLACE) - toggleMediaPanel(FALSE); - - bool is_landmark_info_type = type == LANDMARK; LLPanel* landmark_info_panel = getChild<LLPanel>("landmark_info_panel"); - if (landmark_info_panel) - { - landmark_info_panel->setVisible(is_landmark_info_type); - } + LLPanel* landmark_edit_panel = getChild<LLPanel>("landmark_edit_panel"); + + bool is_info_type_agent = type == AGENT; + bool is_info_type_landmark = type == LANDMARK; + + landmark_info_panel->setVisible(is_info_type_landmark); + landmark_edit_panel->setVisible(is_info_type_landmark || type == CREATE_LANDMARK); + + mRegionInfoDrillIn->setVisible(is_info_type_agent); + mMediaDrillIn->setVisible(is_info_type_agent); switch(type) { @@ -258,6 +265,7 @@ void LLPanelPlaceInfo::setInfoType(INFO_TYPE type) mCurrentTitle = getString("title_create_landmark"); break; + case AGENT: case PLACE: mCurrentTitle = getString("title_place"); @@ -277,6 +285,9 @@ void LLPanelPlaceInfo::setInfoType(INFO_TYPE type) mCurrentTitle = getString("title_place"); break; } + + if (type != PLACE) + toggleMediaPanel(FALSE); } BOOL LLPanelPlaceInfo::isMediaPanelVisible() @@ -366,6 +377,11 @@ void LLPanelPlaceInfo::processParcelInfo(const LLParcelData& parcel_data) } mRating->setValue(rating_icon); + //update for_sale banner, here we should use DFQ_FOR_SALE instead of PF_FOR_SALE + //because we deal with remote parcel response format + bool isForSale = (parcel_data.flags & DFQ_FOR_SALE)? TRUE : FALSE; + getChild<LLIconCtrl>("icon_for_sale")->setVisible(isForSale); + // Just use given region position for display S32 region_x = llround(mPosRegion.mV[0]); S32 region_y = llround(mPosRegion.mV[1]); @@ -436,8 +452,28 @@ void LLPanelPlaceInfo::displayAgentParcelInfo() return; LLParcelData parcel_data; + + // HACK: Converting sim access flags to the format + // returned by remote parcel response. + switch(region->getSimAccess()) + { + case SIM_ACCESS_MATURE: + parcel_data.flags = 0x1; + + case SIM_ACCESS_ADULT: + parcel_data.flags = 0x2; + + default: + parcel_data.flags = 0; + } + + // Adding "For Sale" flag in remote parcel response format. + if (parcel->getForSale()) + { + parcel_data.flags |= DFQ_FOR_SALE; + } + parcel_data.desc = parcel->getDesc(); - parcel_data.flags = region->getSimAccess(); parcel_data.name = parcel->getName(); parcel_data.sim_name = gAgent.getRegion()->getName(); parcel_data.snapshot_id = parcel->getSnapshotID(); @@ -446,6 +482,8 @@ void LLPanelPlaceInfo::displayAgentParcelInfo() parcel_data.global_y = global_pos.mdV[1]; parcel_data.global_z = global_pos.mdV[2]; + + processParcelInfo(parcel_data); } @@ -514,6 +552,28 @@ void LLPanelPlaceInfo::createLandmark(const LLUUID& folder_id) folder_id.notNull() ? folder_id : gInventory.findCategoryUUIDForType(LLAssetType::AT_LANDMARK)); } +void LLPanelPlaceInfo::createPick(const LLVector3d& global_pos) +{ + LLPickData pick_data; + + pick_data.agent_id = gAgent.getID(); + pick_data.session_id = gAgent.getSessionID(); + pick_data.pick_id = LLUUID::generateNewID(); + pick_data.creator_id = gAgentID; + + //legacy var need to be deleted + pick_data.top_pick = FALSE; + pick_data.parcel_id = mParcelID; + pick_data.name = mParcelName->getText(); + pick_data.desc = mDescEditor->getText(); + pick_data.snapshot_id = mSnapshotCtrl->getImageAssetID(); + pick_data.pos_global = global_pos; + pick_data.sort_order = 0; + pick_data.enabled = TRUE; + + LLAvatarPropertiesProcessor::instance().sendDataUpdate(&pick_data, APT_PICK_INFO); +} + void LLPanelPlaceInfo::reshape(S32 width, S32 height, BOOL called_from_parent) { if (mMinHeight > 0 && mScrollingPanel != NULL) diff --git a/indra/newview/llpanelplaceinfo.h b/indra/newview/llpanelplaceinfo.h index e7b81dc3e6..77ce2c6619 100644 --- a/indra/newview/llpanelplaceinfo.h +++ b/indra/newview/llpanelplaceinfo.h @@ -55,9 +55,10 @@ class LLPanelPlaceInfo : public LLPanel, LLRemoteParcelInfoObserver public: enum INFO_TYPE { + AGENT, CREATE_LANDMARK, - PLACE, LANDMARK, + PLACE, TELEPORT_HISTORY }; @@ -80,8 +81,12 @@ public: void setInfoType(INFO_TYPE type); // Create a landmark for the current location - // in a folder specified by folder_id + // in a folder specified by folder_id. void createLandmark(const LLUUID& folder_id); + + // Create a pick for the location specified + // by global_pos. + void createPick(const LLVector3d& global_pos); BOOL isMediaPanelVisible(); void toggleMediaPanel(BOOL visible); @@ -129,6 +134,8 @@ private: LLTextBox* mParcelName; LLTextEditor* mDescEditor; LLIconCtrl* mRating; + LLButton* mRegionInfoDrillIn; + LLButton* mMediaDrillIn; LLTextBox* mOwner; LLTextBox* mCreator; LLTextBox* mCreated; diff --git a/indra/newview/llpanelplaces.cpp b/indra/newview/llpanelplaces.cpp index 7461d150c8..a712c9b9cf 100644 --- a/indra/newview/llpanelplaces.cpp +++ b/indra/newview/llpanelplaces.cpp @@ -43,6 +43,7 @@ #include "lluictrlfactory.h" #include "llagent.h" +#include "lllandmarkactions.h" #include "lllandmarklist.h" #include "llfloaterworldmap.h" #include "llpanelplaces.h" @@ -50,14 +51,23 @@ #include "llpanelteleporthistory.h" #include "llsidetray.h" #include "lltoggleablemenu.h" +#include "llviewermenu.h" #include "llviewerparcelmgr.h" #include "llviewerregion.h" +static const S32 LANDMARK_FOLDERS_MENU_WIDTH = 250; +static const std::string AGENT_INFO_TYPE = "agent"; +static const std::string CREATE_LANDMARK_INFO_TYPE = "create_landmark"; +static const std::string LANDMARK_INFO_TYPE = "landmark"; +static const std::string REMOTE_PLACE_INFO_TYPE = "remote_place"; +static const std::string TELEPORT_HISTORY_INFO_TYPE = "teleport_history"; + // Helper functions static bool cmp_folders(const folder_pair_t& left, const folder_pair_t& right); static std::string getFullFolderName(const LLViewerInventoryCategory* cat); static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats); static const LLVector3 get_pos_local_from_global(const LLVector3d &pos_global); +static void onSLURLBuilt(std::string& slurl); static LLRegisterPanelClassWrapper<LLPanelPlaces> t_places("panel_places"); @@ -68,12 +78,14 @@ LLPanelPlaces::LLPanelPlaces() mFilterEditor(NULL), mPlaceInfo(NULL), mItem(NULL), + mPlaceMenu(NULL), + mLandmarkMenu(NULL), mLandmarkFoldersMenuHandle(), mPosGlobal() { gInventory.addObserver(this); - LLViewerParcelMgr::getInstance()->setAgentParcelChangedCallback( + LLViewerParcelMgr::getInstance()->addAgentParcelChangedCallback( boost::bind(&LLPanelPlaces::onAgentParcelChange, this)); //LLUICtrlFactory::getInstance()->buildPanel(this, "panel_places.xml"); // Called from LLRegisterPanelClass::defaultPanelClassBuilder() @@ -107,7 +119,23 @@ BOOL LLPanelPlaces::postBuild() mOverflowBtn = getChild<LLButton>("overflow_btn"); // *TODO: Assign the action to an appropriate event. - mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::toggleMediaPanel, this)); + //mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::toggleMediaPanel, this)); + mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::onOverflowButtonClicked, this)); + + LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; + registrar.add("Places.OverflowMenu.Action", boost::bind(&LLPanelPlaces::onOverflowMenuItemClicked, this, _2)); + + mPlaceMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_place.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (!mPlaceMenu) + { + llwarns << "Error loading Place menu" << llendl; + } + + mLandmarkMenu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_landmark.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); + if (!mLandmarkMenu) + { + llwarns << "Error loading Landmark menu" << llendl; + } mTabContainer = getChild<LLTabContainer>("Places Tabs"); if (mTabContainer) @@ -122,14 +150,12 @@ BOOL LLPanelPlaces::postBuild() } mPlaceInfo = getChild<LLPanelPlaceInfo>("panel_place_info"); - if (!mPlaceInfo) - return FALSE; LLButton* back_btn = mPlaceInfo->getChild<LLButton>("back_btn"); - if (back_btn) - { - back_btn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this)); - } + back_btn->setClickedCallback(boost::bind(&LLPanelPlaces::onBackButtonClicked, this)); + + // *TODO: Assign the action to an appropriate event. + mOverflowBtn->setClickedCallback(boost::bind(&LLPanelPlaces::toggleMediaPanel, this)); return TRUE; } @@ -147,21 +173,21 @@ void LLPanelPlaces::onOpen(const LLSD& key) togglePlaceInfoPanel(TRUE); updateVerbs(); - if (mPlaceInfoType == "agent") + if (mPlaceInfoType == AGENT_INFO_TYPE) { - mPlaceInfo->setInfoType(LLPanelPlaceInfo::PLACE); + mPlaceInfo->setInfoType(LLPanelPlaceInfo::AGENT); mPlaceInfo->displayAgentParcelInfo(); mPosGlobal = gAgent.getPositionGlobal(); } - else if (mPlaceInfoType == "create_landmark") + else if (mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE) { mPlaceInfo->setInfoType(LLPanelPlaceInfo::CREATE_LANDMARK); mPlaceInfo->displayAgentParcelInfo(); mPosGlobal = gAgent.getPositionGlobal(); } - else if (mPlaceInfoType == "landmark") + else if (mPlaceInfoType == LANDMARK_INFO_TYPE) { LLUUID item_uuid = key["id"].asUUID(); LLInventoryItem* item = gInventory.getItem(item_uuid); @@ -170,7 +196,7 @@ void LLPanelPlaces::onOpen(const LLSD& key) setItem(item); } - else if (mPlaceInfoType == "remote_place") + else if (mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) { if (mPlaceInfo->isMediaPanelVisible()) { @@ -186,19 +212,19 @@ void LLPanelPlaces::onOpen(const LLSD& key) LLUUID(), mPosGlobal); } - else if (mPlaceInfoType == "teleport_history") + else if (mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) { S32 index = key["id"].asInteger(); const LLTeleportHistory::slurl_list_t& hist_items = LLTeleportHistory::getInstance()->getItems(); - LLVector3d pos_global = hist_items[index].mGlobalPos; + mPosGlobal = hist_items[index].mGlobalPos; mPlaceInfo->setInfoType(LLPanelPlaceInfo::TELEPORT_HISTORY); - mPlaceInfo->displayParcelInfo(get_pos_local_from_global(pos_global), + mPlaceInfo->displayParcelInfo(get_pos_local_from_global(mPosGlobal), hist_items[index].mRegionID, - pos_global); + mPosGlobal); } @@ -237,11 +263,10 @@ void LLPanelPlaces::onLandmarkLoaded(LLLandmark* landmark) LLUUID region_id; landmark->getRegionID(region_id); - LLVector3d pos_global; - landmark->getGlobalPos(pos_global); + landmark->getGlobalPos(mPosGlobal); mPlaceInfo->displayParcelInfo(landmark->getRegionPos(), region_id, - pos_global); + mPosGlobal); } void LLPanelPlaces::onFilterEdit(const std::string& search_string) @@ -273,11 +298,6 @@ void LLPanelPlaces::onShareButtonClicked() { // TODO: Launch the "Things" Share wizard } - -void LLPanelPlaces::onCopySLURLButtonClicked() -{ - mActivePanel->onCopySLURL(); -} */ void LLPanelPlaces::onTeleportButtonClicked() @@ -287,13 +307,15 @@ void LLPanelPlaces::onTeleportButtonClicked() if (mPlaceInfo->getVisible()) { - if (mPlaceInfoType == "landmark") + if (mPlaceInfoType == LANDMARK_INFO_TYPE) { LLSD payload; payload["asset_id"] = mItem->getAssetUUID(); LLNotifications::instance().add("TeleportFromLandmark", LLSD(), payload); } - else if (mPlaceInfoType == "remote_place" || mPlaceInfoType == "agent") + else if (mPlaceInfoType == AGENT_INFO_TYPE || + mPlaceInfoType == REMOTE_PLACE_INFO_TYPE || + mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) { LLFloaterWorldMap* worldmap_instance = LLFloaterWorldMap::getInstance(); if (!mPosGlobal.isExactlyZero() && worldmap_instance) @@ -320,9 +342,10 @@ void LLPanelPlaces::onShowOnMapButtonClicked() if(!worldmap_instance) return; - if (mPlaceInfoType == "agent" || - mPlaceInfoType == "create_landmark" || - mPlaceInfoType == "remote_place") + if (mPlaceInfoType == AGENT_INFO_TYPE || + mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE || + mPlaceInfoType == REMOTE_PLACE_INFO_TYPE || + mPlaceInfoType == TELEPORT_HISTORY_INFO_TYPE) { if (!mPosGlobal.isExactlyZero()) { @@ -330,7 +353,7 @@ void LLPanelPlaces::onShowOnMapButtonClicked() LLFloaterReg::showInstance("world_map", "center"); } } - else if (mPlaceInfoType == "landmark") + else if (mPlaceInfoType == LANDMARK_INFO_TYPE) { LLLandmark* landmark = gLandmarkList.getAsset(mItem->getAssetUUID()); if (!landmark) @@ -353,23 +376,104 @@ void LLPanelPlaces::onShowOnMapButtonClicked() } } +void LLPanelPlaces::onOverflowButtonClicked() +{ + LLToggleableMenu* menu; + + bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE; + + if ((is_agent_place_info_visible || + mPlaceInfoType == "remote_place" || + mPlaceInfoType == "teleport_history") && mPlaceMenu != NULL) + { + menu = mPlaceMenu; + + // Enable adding a landmark only for agent current parcel and if + // there is no landmark already pointing to that parcel in agent's inventory. + menu->getChild<LLMenuItemCallGL>("landmark")->setEnabled(is_agent_place_info_visible && + !LLLandmarkActions::landmarkAlreadyExists()); + } + else if (mPlaceInfoType == LANDMARK_INFO_TYPE && mLandmarkMenu != NULL) + { + menu = mLandmarkMenu; + + BOOL is_landmark_removable = FALSE; + if (mItem.notNull()) + { + const LLUUID& item_id = mItem->getUUID(); + const LLUUID& trash_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_TRASH); + is_landmark_removable = gInventory.isObjectDescendentOf(item_id, gInventory.getRootFolderID()) && + !gInventory.isObjectDescendentOf(item_id, trash_id); + } + + menu->getChild<LLMenuItemCallGL>("delete")->setEnabled(is_landmark_removable); + } + else + { + return; + } + + if (!menu->toggleVisibility()) + return; + + menu->updateParent(LLMenuGL::sMenuContainer); + LLRect rect = mOverflowBtn->getRect(); + menu->setButtonRect(rect, this); + LLMenuGL::showPopup(this, menu, rect.mRight, rect.mTop); +} + +void LLPanelPlaces::onOverflowMenuItemClicked(const LLSD& param) +{ + std::string item = param.asString(); + if (item == "landmark") + { + onOpen(LLSD().insert("type", CREATE_LANDMARK_INFO_TYPE)); + } + else if (item == "copy") + { + LLLandmarkActions::getSLURLfromPosGlobal(mPosGlobal, boost::bind(&onSLURLBuilt, _1)); + } + else if (item == "delete") + { + gInventory.removeItem(mItem->getUUID()); + + onBackButtonClicked(); + } + else if (item == "pick") + { + if (!mPlaceInfo) + return; + + mPlaceInfo->createPick(mPosGlobal); + + onBackButtonClicked(); + } +} + void LLPanelPlaces::onCreateLandmarkButtonClicked(const LLUUID& folder_id) { if (!mPlaceInfo) return; mPlaceInfo->createLandmark(folder_id); - - onBackButtonClicked(); - LLSideTray::getInstance()->collapseSideBar(); } void LLPanelPlaces::onBackButtonClicked() { - togglePlaceInfoPanel(FALSE); + if (!mPlaceInfo) + return; + + if (mPlaceInfo->isMediaPanelVisible()) + { + toggleMediaPanel(); + } + else + { + togglePlaceInfoPanel(FALSE); - // Resetting mPlaceInfoType when Place Info panel is closed. - mPlaceInfoType = LLStringUtil::null; + // Resetting mPlaceInfoType when Place Info panel is closed. + mPlaceInfoType = LLStringUtil::null; + } updateVerbs(); } @@ -380,11 +484,11 @@ void LLPanelPlaces::toggleMediaPanel() return; mPlaceInfo->toggleMediaPanel(!mPlaceInfo->isMediaPanelVisible()); - + // Refresh the current place info because // the media panel controls can't refer to // the remote parcel media. - onOpen(LLSD().insert("type", "agent")); + onOpen(LLSD().insert("type", AGENT_INFO_TYPE)); } void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) @@ -402,7 +506,7 @@ void LLPanelPlaces::togglePlaceInfoPanel(BOOL visible) LLRect rect = getRect(); LLRect new_rect = LLRect(rect.mLeft, rect.mTop, rect.mRight, mTabContainer->getRect().mBottom); - mPlaceInfo->reshape(new_rect.getWidth(),new_rect.getHeight()); + mPlaceInfo->reshape(new_rect.getWidth(),new_rect.getHeight()); } } @@ -450,13 +554,13 @@ void LLPanelPlaces::onAgentParcelChange() if (!mPlaceInfo) return; - if (mPlaceInfo->getVisible() && mPlaceInfoType == "create_landmark") + if (mPlaceInfo->getVisible() && mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE) { onOpen(LLSD().insert("type", mPlaceInfoType)); } else if (mPlaceInfo->isMediaPanelVisible()) { - onOpen(LLSD().insert("type", "agent")); + onOpen(LLSD().insert("type", AGENT_INFO_TYPE)); } else { @@ -470,8 +574,8 @@ void LLPanelPlaces::updateVerbs() return; bool is_place_info_visible = mPlaceInfo->getVisible(); - bool is_agent_place_info_visible = mPlaceInfoType == "agent"; - bool is_create_landmark_visible = mPlaceInfoType == "create_landmark"; + bool is_agent_place_info_visible = mPlaceInfoType == AGENT_INFO_TYPE; + bool is_create_landmark_visible = mPlaceInfoType == CREATE_LANDMARK_INFO_TYPE; bool is_media_panel_visible = mPlaceInfo->isMediaPanelVisible(); mTeleportBtn->setVisible(!is_create_landmark_visible); @@ -479,9 +583,7 @@ void LLPanelPlaces::updateVerbs() mCreateLandmarkBtn->setVisible(is_create_landmark_visible); mFolderMenuBtn->setVisible(is_create_landmark_visible); - // Enable overflow button only when showing the information - // about agent's current location. - mOverflowBtn->setEnabled(is_agent_place_info_visible); + mOverflowBtn->setEnabled(is_place_info_visible && !is_media_panel_visible && !is_create_landmark_visible); if (is_place_info_visible) { @@ -493,7 +595,15 @@ void LLPanelPlaces::updateVerbs() !mPosGlobal.isExactlyZero() && !LLViewerParcelMgr::getInstance()->inAgentParcel(mPosGlobal)); } - else if (mPlaceInfoType == "landmark" || mPlaceInfoType == "remote_place") + else if (is_create_landmark_visible) + { + // Enable "Create Landmark" only if there is no landmark + // for the current parcel. + bool no_landmark = !LLLandmarkActions::landmarkAlreadyExists(); + mCreateLandmarkBtn->setEnabled(no_landmark); + mFolderMenuBtn->setEnabled(no_landmark); + } + else if (mPlaceInfoType == LANDMARK_INFO_TYPE || mPlaceInfoType == REMOTE_PLACE_INFO_TYPE) { mTeleportBtn->setEnabled(TRUE); } @@ -515,6 +625,7 @@ void LLPanelPlaces::showLandmarkFoldersMenu() menu_p.can_tear_off(false); menu_p.visible(false); menu_p.scrollable(true); + menu_p.max_scrollable_items = 10; LLToggleableMenu* menu = LLUICtrlFactory::create<LLToggleableMenu>(menu_p); @@ -525,18 +636,8 @@ void LLPanelPlaces::showLandmarkFoldersMenu() if(!menu) return; - if (menu->getClosedByButtonClick()) - { - menu->resetClosedByButtonClick(); - return; - } - - if (menu->getVisible()) - { - menu->setVisible(FALSE); - menu->resetClosedByButtonClick(); + if (!menu->toggleVisibility()) return; - } // Collect all folders that can contain landmarks. LLInventoryModel::cat_array_t cats; @@ -591,7 +692,6 @@ void LLPanelPlaces::showLandmarkFoldersMenu() mLandmarkFoldersCache = folders; menu->empty(); - U32 max_width = 0; // Menu width must not exceed the root view limits, // so we assume the space between the left edge of @@ -599,6 +699,7 @@ void LLPanelPlaces::showLandmarkFoldersMenu() LLRect screen_btn_rect; localRectToScreen(btn_rect, &screen_btn_rect); S32 free_space = screen_btn_rect.mRight; + U32 max_width = llmin(LANDMARK_FOLDERS_MENU_WIDTH, free_space); for(folder_vec_t::const_iterator it = mLandmarkFoldersCache.begin(); it != mLandmarkFoldersCache.end(); it++) { @@ -612,13 +713,14 @@ void LLPanelPlaces::showLandmarkFoldersMenu() LLMenuItemCallGL *menu_item = LLUICtrlFactory::create<LLMenuItemCallGL>(item_params); + // *TODO: Use a separate method for menu width calculation. // Check whether item name wider than menu - if ((S32) menu_item->getNominalWidth() > free_space) + if (menu_item->getNominalWidth() > max_width) { S32 chars_total = item_name.length(); S32 chars_fitted = 1; menu_item->setLabel(LLStringExplicit("")); - S32 label_space = free_space - menu_item->getFont()->getWidth("...") - + S32 label_space = max_width - menu_item->getFont()->getWidth("...") - menu_item->getNominalWidth(); // This returns width of menu item with empty label (pad pixels) while (chars_fitted < chars_total && menu_item->getFont()->getWidth(item_name, 0, chars_fitted) < label_space) @@ -630,8 +732,6 @@ void LLPanelPlaces::showLandmarkFoldersMenu() menu_item->setLabel(item_name.substr(0, chars_fitted) + "..."); } - max_width = llmax(max_width, menu_item->getNominalWidth()); - menu->addChild(menu_item); } @@ -705,7 +805,7 @@ static void collectLandmarkFolders(LLInventoryModel::cat_array_t& cats) } } -const LLVector3 get_pos_local_from_global(const LLVector3d &pos_global) +static const LLVector3 get_pos_local_from_global(const LLVector3d &pos_global) { F32 region_x = (F32)fmod( pos_global.mdV[VX], (F64)REGION_WIDTH_METERS ); F32 region_y = (F32)fmod( pos_global.mdV[VY], (F64)REGION_WIDTH_METERS ); @@ -713,3 +813,13 @@ const LLVector3 get_pos_local_from_global(const LLVector3d &pos_global) LLVector3 pos_local(region_x, region_y, (F32)pos_global.mdV[VZ]); return pos_local; } + +static void onSLURLBuilt(std::string& slurl) +{ + LLView::getWindow()->copyTextToClipboard(utf8str_to_wstring(slurl)); + + LLSD args; + args["SLURL"] = slurl; + + LLNotifications::instance().add("CopySLURL", args); +} diff --git a/indra/newview/llpanelplaces.h b/indra/newview/llpanelplaces.h index 431c8168d9..089a854762 100644 --- a/indra/newview/llpanelplaces.h +++ b/indra/newview/llpanelplaces.h @@ -66,10 +66,11 @@ private: void onFilterEdit(const std::string& search_string); void onTabSelected(); - //void onCopySLURLButtonClicked(); //void onShareButtonClicked(); void onTeleportButtonClicked(); void onShowOnMapButtonClicked(); + void onOverflowButtonClicked(); + void onOverflowMenuItemClicked(const LLSD& param); void onCreateLandmarkButtonClicked(const LLUUID& folder_id); void onBackButtonClicked(); @@ -85,6 +86,8 @@ private: LLPanelPlacesTab* mActivePanel; LLTabContainer* mTabContainer; LLPanelPlaceInfo* mPlaceInfo; + LLToggleableMenu* mPlaceMenu; + LLToggleableMenu* mLandmarkMenu; LLButton* mCreateLandmarkBtn; LLButton* mFolderMenuBtn; diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp index 017a7312a1..6d3d307526 100644 --- a/indra/newview/llpanelprofile.cpp +++ b/indra/newview/llpanelprofile.cpp @@ -49,7 +49,7 @@ public: LLAgentHandler() : LLCommandHandler("agent", true) { } bool handle(const LLSD& params, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { if (params.size() < 2) return false; LLUUID agent_id; @@ -95,6 +95,8 @@ void LLPanelProfile::onOpen(const LLSD& key) { if (key.has("open_tab_name")) { + getTabContainer()[PANEL_PICKS]->onClose(); + // onOpen from selected panel will be called from onTabSelected callback getTabCtrl()->selectTabByName(key["open_tab_name"]); } @@ -102,6 +104,10 @@ void LLPanelProfile::onOpen(const LLSD& key) { getTabCtrl()->getCurrentPanel()->onOpen(getAvatarId()); } + + // Update the avatar name. + gCacheName->get(getAvatarId(), FALSE, + boost::bind(&LLPanelProfile::onAvatarNameCached, this, _1, _2, _3, _4)); } //*TODO redo panel toggling @@ -135,6 +141,7 @@ void LLPanelProfile::togglePanel(LLPanel* panel) else { this->setAllChildrenVisible(TRUE); + panel->setVisible(FALSE); if (panel->getParent() == this) { removeChild(panel); @@ -163,3 +170,9 @@ void LLPanelProfile::setAllChildrenVisible(BOOL visible) viewp->setVisible(visible); } } + +void LLPanelProfile::onAvatarNameCached(const LLUUID& id, const std::string& first_name, const std::string& last_name, BOOL is_group) +{ + llassert(getAvatarId() == id); + getChild<LLTextBox>("user_name", FALSE)->setValue(first_name + " " + last_name); +} diff --git a/indra/newview/llpanelprofile.h b/indra/newview/llpanelprofile.h index b55963ec4a..0864ec1bc3 100644 --- a/indra/newview/llpanelprofile.h +++ b/indra/newview/llpanelprofile.h @@ -72,6 +72,14 @@ protected: profile_tabs_t& getTabContainer() { return mTabContainer; } private: + // LLCacheName will call this function when avatar name is loaded from server. + // This is required to display names that have not been cached yet. + void onAvatarNameCached( + const LLUUID& id, + const std::string& first_name, + const std::string& last_name, + BOOL is_group); + LLTabContainer* mTabCtrl; profile_tabs_t mTabContainer; LLUUID mAvatarId; diff --git a/indra/newview/llpanelprofileview.cpp b/indra/newview/llpanelprofileview.cpp index cd18dc4462..18184a6476 100644 --- a/indra/newview/llpanelprofileview.cpp +++ b/indra/newview/llpanelprofileview.cpp @@ -41,10 +41,10 @@ static LLRegisterPanelClassWrapper<LLPanelProfileView> t_panel_target_profile("panel_profile_view"); static std::string PANEL_NOTES = "panel_notes"; +static const std::string PANEL_PROFILE = "panel_profile"; LLPanelProfileView::LLPanelProfileView() : LLPanelProfile() -, mCacheNameCallbackConnected(false) { } @@ -66,21 +66,6 @@ void LLPanelProfileView::onOpen(const LLSD& key) } LLPanelProfile::onOpen(key); - - // *HACK Profile View is created before gCacheName, as a result we can't call addObserver() - // in postBuild() and have to connect callback here. - // This will call addObserver() once per LLPanelProfileView instance. - if(!mCacheNameCallbackConnected) - { - gCacheName->addObserver(boost::bind(&LLPanelProfileView::cacheNameCallback, this, _1, _2, _3, _4)); - mCacheNameCallbackConnected = true; - } - - // getFullName() will return "(Loading...)" for non cached names, - // in this case cacheNameCallback() will resolve the name. - std::string full_name; - gCacheName->getFullName(getAvatarId(),full_name); - childSetValue("user_name",full_name); } BOOL LLPanelProfileView::postBuild() @@ -88,6 +73,10 @@ BOOL LLPanelProfileView::postBuild() LLPanelProfile::postBuild(); getTabContainer()[PANEL_NOTES] = getChild<LLPanelAvatarNotes>(PANEL_NOTES); + + //*TODO remove this, according to style guide we don't use status combobox + getTabContainer()[PANEL_PROFILE]->childSetVisible("online_me_status_text", FALSE); + getTabContainer()[PANEL_PROFILE]->childSetVisible("status_combo", FALSE); childSetCommitCallback("back",boost::bind(&LLPanelProfileView::onBackBtnClick,this),NULL); @@ -105,11 +94,3 @@ void LLPanelProfileView::onBackBtnClick() parent->openPreviousPanel(); } } - -void LLPanelProfileView::cacheNameCallback(const LLUUID& id, const std::string& first_name, const std::string& last_name, BOOL is_group) -{ - if(getAvatarId() == id) - { - childSetValue("user_name", first_name + " " + last_name); - } -} diff --git a/indra/newview/llpanelprofileview.h b/indra/newview/llpanelprofileview.h index 4d5e2997c1..92def7b7ca 100644 --- a/indra/newview/llpanelprofileview.h +++ b/indra/newview/llpanelprofileview.h @@ -60,21 +60,9 @@ public: /*virtual*/ BOOL postBuild(); - // LLCacheName will call this function when avatar name is loaded from server. - // This is required to display names that have not been cached yet. - void cacheNameCallback( - const LLUUID& id, - const std::string& first_name, - const std::string& last_name, - BOOL is_group); - protected: void onBackBtnClick(); - -private: - - bool mCacheNameCallbackConnected; }; #endif //LL_LLPANELPROFILEVIEW_H diff --git a/indra/newview/llpanelteleporthistory.cpp b/indra/newview/llpanelteleporthistory.cpp index 51cd05376a..8b378c33e3 100644 --- a/indra/newview/llpanelteleporthistory.cpp +++ b/indra/newview/llpanelteleporthistory.cpp @@ -200,7 +200,7 @@ void LLTeleportHistoryPanel::showTeleportHistory() index_column["type"] = "text"; index_column["value"] = index; - mHistoryItems->addElement(row); + mHistoryItems->addElement(row, ADD_TOP); if (cur_item == index) { diff --git a/indra/newview/llpanelvolume.cpp b/indra/newview/llpanelvolume.cpp index 1e44a294b0..ce16b95bc0 100644 --- a/indra/newview/llpanelvolume.cpp +++ b/indra/newview/llpanelvolume.cpp @@ -49,7 +49,6 @@ #include "material_codes.h" // project includes -#include "llagent.h" #include "llbutton.h" #include "llcheckboxctrl.h" #include "llcolorswatch.h" diff --git a/indra/newview/llpreviewnotecard.cpp b/indra/newview/llpreviewnotecard.cpp index ce9fcd9da2..cadab71ba8 100644 --- a/indra/newview/llpreviewnotecard.cpp +++ b/indra/newview/llpreviewnotecard.cpp @@ -101,14 +101,6 @@ BOOL LLPreviewNotecard::postBuild() childSetText("desc", item->getDescription()); childSetPrevalidate("desc", &LLLineEditor::prevalidatePrintableNotPipe); - LLViewerTextEditor* editor = getChild<LLViewerTextEditor>("Notecard Editor"); - - if (editor) - { - editor->setWordWrap(TRUE); - editor->setHandleEditKeysDirectly(TRUE); - } - return LLPreview::postBuild(); } diff --git a/indra/newview/llpreviewscript.cpp b/indra/newview/llpreviewscript.cpp index ad978cc5b3..6c73638475 100644 --- a/indra/newview/llpreviewscript.cpp +++ b/indra/newview/llpreviewscript.cpp @@ -83,7 +83,7 @@ #include "llviewertexteditor.h" #include "llviewerwindow.h" #include "lluictrlfactory.h" -#include "llwebbrowserctrl.h" +#include "llmediactrl.h" #include "lluictrlfactory.h" #include "llviewercontrol.h" @@ -340,10 +340,6 @@ BOOL LLScriptEdCore::postBuild() childSetCommitCallback("Insert...", &LLScriptEdCore::onBtnInsertFunction, this); mEditor = getChild<LLViewerTextEditor>("Script Editor"); - mEditor->setFollowsAll(); - mEditor->setHandleEditKeysDirectly(TRUE); - mEditor->setEnabled(TRUE); - mEditor->setWordWrap(TRUE); childSetCommitCallback("lsl errors", &LLScriptEdCore::onErrorList, this); childSetAction("Save_btn", boost::bind(&LLScriptEdCore::doSave,this,FALSE)); @@ -450,7 +446,7 @@ void LLScriptEdCore::updateDynamicHelp(BOOL immediate) // update back and forward buttons LLButton* fwd_button = help_floater->getChild<LLButton>("fwd_btn"); LLButton* back_button = help_floater->getChild<LLButton>("back_btn"); - LLWebBrowserCtrl* browser = help_floater->getChild<LLWebBrowserCtrl>("lsl_guide_html"); + LLMediaCtrl* browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); back_button->setEnabled(browser->canNavigateBack()); fwd_button->setEnabled(browser->canNavigateForward()); @@ -459,12 +455,12 @@ void LLScriptEdCore::updateDynamicHelp(BOOL immediate) return; } - const LLTextSegment* segment = NULL; - std::vector<const LLTextSegment*> selected_segments; + LLTextSegmentPtr segment = NULL; + std::vector<LLTextSegmentPtr> selected_segments; mEditor->getSelectedSegments(selected_segments); // try segments in selection range first - std::vector<const LLTextSegment*>::iterator segment_iter; + std::vector<LLTextSegmentPtr>::iterator segment_iter; for (segment_iter = selected_segments.begin(); segment_iter != selected_segments.end(); ++segment_iter) { if((*segment_iter)->getToken() && (*segment_iter)->getToken()->getType() == LLKeywordToken::WORD) @@ -477,7 +473,7 @@ void LLScriptEdCore::updateDynamicHelp(BOOL immediate) // then try previous segment in case we just typed it if (!segment) { - const LLTextSegment* test_segment = mEditor->getPreviousSegment(); + const LLTextSegmentPtr test_segment = mEditor->getPreviousSegment(); if(test_segment->getToken() && test_segment->getToken()->getType() == LLKeywordToken::WORD) { segment = test_segment; @@ -509,7 +505,7 @@ void LLScriptEdCore::setHelpPage(const std::string& help_string) LLFloater* help_floater = mLiveHelpHandle.get(); if (!help_floater) return; - LLWebBrowserCtrl* web_browser = help_floater->getChild<LLWebBrowserCtrl>("lsl_guide_html"); + LLMediaCtrl* web_browser = help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); if (!web_browser) return; LLComboBox* history_combo = help_floater->getChild<LLComboBox>("history_combo"); @@ -650,7 +646,7 @@ void LLScriptEdCore::onBtnDynamicHelp() live_help_floater->childSetAction("back_btn", onClickBack, this); live_help_floater->childSetAction("fwd_btn", onClickForward, this); - LLWebBrowserCtrl* browser = live_help_floater->getChild<LLWebBrowserCtrl>("lsl_guide_html"); + LLMediaCtrl* browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); browser->setAlwaysRefresh(TRUE); LLComboBox* help_combo = live_help_floater->getChild<LLComboBox>("history_combo"); @@ -679,7 +675,7 @@ void LLScriptEdCore::onClickBack(void* userdata) LLFloater* live_help_floater = corep->mLiveHelpHandle.get(); if (live_help_floater) { - LLWebBrowserCtrl* browserp = live_help_floater->getChild<LLWebBrowserCtrl>("lsl_guide_html"); + LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); if (browserp) { browserp->navigateBack(); @@ -694,7 +690,7 @@ void LLScriptEdCore::onClickForward(void* userdata) LLFloater* live_help_floater = corep->mLiveHelpHandle.get(); if (live_help_floater) { - LLWebBrowserCtrl* browserp = live_help_floater->getChild<LLWebBrowserCtrl>("lsl_guide_html"); + LLMediaCtrl* browserp = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); if (browserp) { browserp->navigateForward(); @@ -736,7 +732,7 @@ void LLScriptEdCore::onHelpComboCommit(LLUICtrl* ctrl, void* userdata) corep->addHelpItemToHistory(help_string); - LLWebBrowserCtrl* web_browser = live_help_floater->getChild<LLWebBrowserCtrl>("lsl_guide_html"); + LLMediaCtrl* web_browser = live_help_floater->getChild<LLMediaCtrl>("lsl_guide_html"); LLUIString url_string = gSavedSettings.getString("LSLHelpURL"); url_string.setArg("[LSL_STRING]", help_string); web_browser->navigateTo(url_string); diff --git a/indra/newview/llpreviewsound.cpp b/indra/newview/llpreviewsound.cpp index 84f7562297..0f2b94ebd4 100644 --- a/indra/newview/llpreviewsound.cpp +++ b/indra/newview/llpreviewsound.cpp @@ -32,7 +32,7 @@ #include "llviewerprecompiledheaders.h" -#include "audioengine.h" +#include "llaudioengine.h" #include "llagent.h" // gAgent #include "llbutton.h" #include "llinventory.h" diff --git a/indra/newview/llscreenchannel.cpp b/indra/newview/llscreenchannel.cpp index 10561f5701..68996673be 100644 --- a/indra/newview/llscreenchannel.cpp +++ b/indra/newview/llscreenchannel.cpp @@ -49,15 +49,14 @@ using namespace LLNotificationsUI; bool LLScreenChannel::mWasStartUpToastShown = false; //-------------------------------------------------------------------------- -LLScreenChannel::LLScreenChannel(): mOverflowToastPanel(NULL), - mStartUpToastPanel(NULL), - mToastAlignment(NA_BOTTOM), - mCanStoreToasts(true), - mHiddenToastsNum(0), - mOverflowToastHidden(false), - mIsHovering(false), - mControlHovering(false) +LLScreenChannel::LLScreenChannel(LLUUID& id): mOverflowToastPanel(NULL), mStartUpToastPanel(NULL), + mToastAlignment(NA_BOTTOM), mCanStoreToasts(true), + mHiddenToastsNum(0), mOverflowToastHidden(false), + mIsHovering(false), mControlHovering(false), + mShowToasts(false) { + mID = id; + setFollows(FOLLOWS_RIGHT | FOLLOWS_BOTTOM | FOLLOWS_TOP); mOverflowFormatString = LLTrans::getString("OverflowInfoChannelString"); @@ -89,22 +88,11 @@ void LLScreenChannel::reshape(S32 width, S32 height, BOOL called_from_parent) //-------------------------------------------------------------------------- void LLScreenChannel::addToast(LLToast::Params p) { - bool isSysWellWndShown = LLFloaterReg::getInstance("syswell_window")->getVisible(); - // we show toast in the following cases: - // - the StartUp Toast is already hidden and the SysWell's window is hidden - // - the SysWell's window is shown, but notification is a tip. We can't store it, so we show it - // - the channel has a CENTRE allignment, so it is intended for alerts. We always show alerts - bool show_toast = (mWasStartUpToastShown && !isSysWellWndShown) || (isSysWellWndShown && p.is_tip) || mToastAlignment == NA_CENTRE; - bool store_toast = !show_toast && !p.is_tip && mCanStoreToasts; - - // if we can't show or store a toast, then do nothing, just send ignore to a notification - if(!show_toast && !store_toast) + bool store_toast = !mShowToasts && p.can_be_stored && mCanStoreToasts; + + if(!mShowToasts && !store_toast) { - if(p.notification) - { - p.notification->setIgnored(TRUE); - p.notification->respond(p.notification->getResponseTemplate()); - } + mOnRejectToast(p); return; } @@ -112,14 +100,13 @@ void LLScreenChannel::addToast(LLToast::Params p) mOverflowToastHidden = false; - getRootView()->addChild(new_toast_elem.toast); new_toast_elem.toast->setOnFadeCallback(boost::bind(&LLScreenChannel::onToastFade, this, new_toast_elem.toast)); if(mControlHovering) { new_toast_elem.toast->setOnToastHoverCallback(boost::bind(&LLScreenChannel::onToastHover, this, _1, _2)); } - if(show_toast) + if(mShowToasts) { mToastList.push_back(new_toast_elem); showToasts(); @@ -136,8 +123,7 @@ void LLScreenChannel::onToastFade(LLToast* toast) { std::vector<ToastElem>::iterator it = find(mToastList.begin(), mToastList.end(), static_cast<LLPanel*>(toast)); - // *TODO: toast->isViewed() - seems unnecessary - bool destroy_toast = toast->isViewed() || !mCanStoreToasts || !toast->getCanBeStored(); + bool destroy_toast = !mCanStoreToasts || !toast->getCanBeStored(); if(destroy_toast) { mToastList.erase(it); @@ -369,7 +355,8 @@ void LLScreenChannel::showToastsTop() void LLScreenChannel::createOverflowToast(S32 bottom, F32 timer) { LLRect toast_rect; - LLToast::Params p; // *TODO: fill structure + LLToast::Params p; + p.timer_period = timer; mOverflowToastPanel = new LLToast(p); if(!mOverflowToastPanel) @@ -393,8 +380,6 @@ void LLScreenChannel::createOverflowToast(S32 bottom, F32 timer) mOverflowToastPanel->reshape(getRect().getWidth(), toast_rect.getHeight(), true); toast_rect.setLeftTopAndSize(getRect().mLeft, bottom + toast_rect.getHeight()+gSavedSettings.getS32("ToastMargin"), getRect().getWidth(), toast_rect.getHeight()); mOverflowToastPanel->setRect(toast_rect); - mOverflowToastPanel->setAndStartTimer(timer); - getRootView()->addChild(mOverflowToastPanel); text_box->setValue(text); text_box->setVisible(TRUE); @@ -407,7 +392,6 @@ void LLScreenChannel::createOverflowToast(S32 bottom, F32 timer) void LLScreenChannel::onOverflowToastHide() { mOverflowToastHidden = true; - // *TODO: check whether it is needed: closeOverflowToastPanel(); } //-------------------------------------------------------------------------- @@ -415,7 +399,7 @@ void LLScreenChannel::closeOverflowToastPanel() { if(mOverflowToastPanel != NULL) { - mOverflowToastPanel->close(); + mOverflowToastPanel->closeFloater(); mOverflowToastPanel = NULL; } } @@ -424,7 +408,8 @@ void LLScreenChannel::closeOverflowToastPanel() void LLScreenChannel::createStartUpToast(S32 notif_num, S32 bottom, F32 timer) { LLRect toast_rect; - LLToast::Params p; // *TODO: fill structure + LLToast::Params p; + p.timer_period = timer; mStartUpToastPanel = new LLToast(p); if(!mStartUpToastPanel) @@ -453,8 +438,6 @@ void LLScreenChannel::createStartUpToast(S32 notif_num, S32 bottom, F32 timer) mStartUpToastPanel->reshape(getRect().getWidth(), toast_rect.getHeight(), true); toast_rect.setLeftTopAndSize(getRect().mLeft, bottom + toast_rect.getHeight()+gSavedSettings.getS32("ToastMargin"), getRect().getWidth(), toast_rect.getHeight()); mStartUpToastPanel->setRect(toast_rect); - mStartUpToastPanel->setAndStartTimer(timer); - getRootView()->addChild(mStartUpToastPanel); text_box->setValue(text); text_box->setVisible(TRUE); @@ -480,8 +463,7 @@ void LLScreenChannel::closeStartUpToast() { if(mStartUpToastPanel != NULL) { - LLScreenChannel::setStartUpToastShown(); - mStartUpToastPanel->close(); + mStartUpToastPanel->closeFloater(); mStartUpToastPanel = NULL; } } @@ -500,8 +482,8 @@ void LLScreenChannel::removeToastsFromChannel() hideToastsFromScreen(); for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++) { - (*it).toast->close(); - //toast->mOnToastDestroy(toast, LLSD()); //TODO: check OnToastDestroy handler for chat + // *TODO: ivestigate mOnToastDestroy callback - change name or/and place + (*it).toast->mOnToastDestroy((*it).toast); } mToastList.clear(); } @@ -515,9 +497,12 @@ void LLScreenChannel::removeAndStoreAllVisibleToasts() hideToastsFromScreen(); for(std::vector<ToastElem>::iterator it = mToastList.begin(); it != mToastList.end(); it++) { - mStoredToastList.push_back(*it); - mOnStoreToast((*it).toast->getPanel(), (*it).id); - (*it).toast->stopTimer(); + if((*it).toast->getCanBeStored()) + { + mStoredToastList.push_back(*it); + mOnStoreToast((*it).toast->getPanel(), (*it).id); + (*it).toast->stopTimer(); + } (*it).toast->setVisible(FALSE); } diff --git a/indra/newview/llscreenchannel.h b/indra/newview/llscreenchannel.h index 1ca70c72d0..746580b574 100644 --- a/indra/newview/llscreenchannel.h +++ b/indra/newview/llscreenchannel.h @@ -56,7 +56,7 @@ class LLScreenChannel : public LLUICtrl { friend class LLChannelManager; public: - LLScreenChannel(); + LLScreenChannel(LLUUID& id); virtual ~LLScreenChannel(); // Channel's outfit-functions @@ -96,22 +96,33 @@ public: // Channel's behavior-functions // set whether a channel will control hovering inside itself or not - void setControlHovering(bool control) { mControlHovering = control; } + void setControlHovering(bool control) { mControlHovering = control; } // set Hovering flag for a channel - void setHovering(bool hovering) { mIsHovering = hovering; } + void setHovering(bool hovering) { mIsHovering = hovering; } // set whether a channel will store faded toasts or not - void setCanStoreToasts(bool store) { mCanStoreToasts = store; } + void setCanStoreToasts(bool store) { mCanStoreToasts = store; } // tell all channels that the StartUp toast was shown and allow them showing of toasts static void setStartUpToastShown() { mWasStartUpToastShown = true; } - // + // get StartUp Toast's state static bool getStartUpToastShown() { return mWasStartUpToastShown; } + // set mode for dislaying of toasts + void setDisplayToastsAlways(bool display_toasts) { mDisplayToastsAlways = display_toasts; } + // get mode for dislaying of toasts + bool getDisplayToastsAlways() { return mDisplayToastsAlways; } + // tell a channel to show toasts or not + void setShowToasts(bool show) { mShowToasts = show; } + // determine whether channel shows toasts or not + bool getShowToasts() { return mShowToasts; } // Channel's other interface functions functions // get number of hidden notifications from a channel S32 getNumberOfHiddenToasts() { return mHiddenToastsNum;} // update number of notifications in the StartUp Toast void updateStartUpString(S32 num); + // get toast allignment preset for a channel e_notification_toast_alignment getToastAlignment() {return mToastAlignment;} + // get ID of a channel + LLUUID getChannelID() { return mID; } // Channel's callbacks // callback for storing of faded toasts @@ -119,6 +130,11 @@ public: typedef boost::signals2::signal<void (LLPanel* info_panel, const LLUUID id)> store_tost_signal_t; store_tost_signal_t mOnStoreToast; boost::signals2::connection setOnStoreToastCallback(store_tost_callback_t cb) { return mOnStoreToast.connect(cb); } + // callback for discarding of a rejected toast + typedef boost::function<void (LLToast::Params p)> reject_tost_callback_t; + typedef boost::signals2::signal<void (LLToast::Params p)> reject_tost_signal_t; + reject_tost_signal_t mOnRejectToast; + boost::signals2::connection setOnRejectToastCallback(reject_tost_callback_t cb) { return mOnRejectToast.connect(cb); } private: struct ToastElem @@ -173,7 +189,10 @@ private: bool mControlHovering; bool mIsHovering; bool mCanStoreToasts; + bool mDisplayToastsAlways; bool mOverflowToastHidden; + // controls whether a channel shows toasts or not + bool mShowToasts; // e_notification_toast_alignment mToastAlignment; @@ -185,6 +204,9 @@ private: // attributes for the StartUp Toast LLToast* mStartUpToastPanel; + // channel's ID + LLUUID mID; + std::vector<ToastElem> mToastList; std::vector<ToastElem> mStoredToastList; std::map<LLToast*, bool> mToastEventStack; diff --git a/indra/newview/llselectmgr.cpp b/indra/newview/llselectmgr.cpp index 5984af5df4..32fe996125 100644 --- a/indra/newview/llselectmgr.cpp +++ b/indra/newview/llselectmgr.cpp @@ -60,6 +60,7 @@ #include "llfloaterreg.h" #include "llfloatertools.h" #include "llframetimer.h" +#include "llfocusmgr.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" #include "llinventorymodel.h" @@ -75,6 +76,8 @@ #include "llviewercamera.h" #include "llviewercontrol.h" #include "llviewertexturelist.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" #include "llviewermenu.h" #include "llviewerobject.h" #include "llviewerobjectlist.h" @@ -774,7 +777,7 @@ void LLSelectMgr::addAsIndividual(LLViewerObject *objectp, S32 face, BOOL undoab } -LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp) +LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp, S32 face) { // Always blitz hover list when setting mHoverObjects->deleteAllNodes(); @@ -806,6 +809,7 @@ LLObjectSelectionHandle LLSelectMgr::setHoverObject(LLViewerObject *objectp) { LLViewerObject* cur_objectp = *iter; LLSelectNode* nodep = new LLSelectNode(cur_objectp, FALSE); + nodep->selectTE(face, TRUE); mHoverObjects->addNodeAtEnd(nodep); } @@ -830,8 +834,8 @@ void LLSelectMgr::highlightObjectOnly(LLViewerObject* objectp) return; } - if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !objectp->permYouOwner()) || - (gSavedSettings.getBOOL("SelectMovableOnly") && !objectp->permMove())) + if ((gSavedSettings.getBOOL("SelectOwnedOnly") && !objectp->permYouOwner()) + || (gSavedSettings.getBOOL("SelectMovableOnly") && !objectp->permMove())) { // only select my own objects return; @@ -4565,54 +4569,7 @@ void LLSelectMgr::updateSilhouettes() std::vector<LLViewerObject*> changed_objects; - if (mSelectedObjects->getNumNodes()) - { - //gGLSPipelineSelection.set(); - - //mSilhouetteImagep->bindTexture(); - //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); - - for (S32 pass = 0; pass < 2; pass++) - { - for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); - iter != mSelectedObjects->end(); iter++) - { - LLSelectNode* node = *iter; - LLViewerObject* objectp = node->getObject(); - if (!objectp) - continue; - // do roots first, then children so that root flags are cleared ASAP - BOOL roots_only = (pass == 0); - BOOL is_root = (objectp->isRootEdit()); - if (roots_only != is_root || objectp->mDrawable.isNull()) - { - continue; - } - - if (!node->mSilhouetteExists - || objectp->isChanged(LLXform::SILHOUETTE) - || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) - { - if (num_sils_genned++ < MAX_SILS_PER_FRAME)// && objectp->mDrawable->isVisible()) - { - generateSilhouette(node, LLViewerCamera::getInstance()->getOrigin()); - changed_objects.push_back(objectp); - } - else if (objectp->isAttachment()) - { - //RN: hack for orthogonal projection of HUD attachments - LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); - if (attachment_pt && attachment_pt->getIsHUDAttachment()) - { - LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); - generateSilhouette(node, camera_pos); - } - } - } - } - } - } - + updateSelectionSilhouette(mSelectedObjects, num_sils_genned, changed_objects); if (mRectSelectedObjects.size() > 0) { //gGLSPipelineSelection.set(); @@ -4806,6 +4763,56 @@ void LLSelectMgr::updateSilhouettes() //gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT); } +void LLSelectMgr::updateSelectionSilhouette(LLObjectSelectionHandle object_handle, S32& num_sils_genned, std::vector<LLViewerObject*>& changed_objects) +{ + if (object_handle->getNumNodes()) + { + //gGLSPipelineSelection.set(); + + //mSilhouetteImagep->bindTexture(); + //glAlphaFunc(GL_GREATER, sHighlightAlphaTest); + + for (S32 pass = 0; pass < 2; pass++) + { + for (LLObjectSelection::iterator iter = object_handle->begin(); + iter != object_handle->end(); iter++) + { + LLSelectNode* node = *iter; + LLViewerObject* objectp = node->getObject(); + if (!objectp) + continue; + // do roots first, then children so that root flags are cleared ASAP + BOOL roots_only = (pass == 0); + BOOL is_root = (objectp->isRootEdit()); + if (roots_only != is_root || objectp->mDrawable.isNull()) + { + continue; + } + + if (!node->mSilhouetteExists + || objectp->isChanged(LLXform::SILHOUETTE) + || (objectp->getParent() && objectp->getParent()->isChanged(LLXform::SILHOUETTE))) + { + if (num_sils_genned++ < MAX_SILS_PER_FRAME)// && objectp->mDrawable->isVisible()) + { + generateSilhouette(node, LLViewerCamera::getInstance()->getOrigin()); + changed_objects.push_back(objectp); + } + else if (objectp->isAttachment()) + { + //RN: hack for orthogonal projection of HUD attachments + LLViewerJointAttachment* attachment_pt = (LLViewerJointAttachment*)objectp->getRootEdit()->mDrawable->getParent(); + if (attachment_pt && attachment_pt->getIsHUDAttachment()) + { + LLVector3 camera_pos = LLVector3(-10000.f, 0.f, 0.f); + generateSilhouette(node, camera_pos); + } + } + } + } + } + } +} void LLSelectMgr::renderSilhouettes(BOOL for_hud) { if (!mRenderSilhouettes) @@ -4848,6 +4855,7 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { inspect_item_id = inspect_instance->getSelectedUUID(); } + LLUUID focus_item_id = LLViewerMediaFocus::getInstance()->getSelectedUUID(); for (S32 pass = 0; pass < 2; pass++) { for (LLObjectSelection::iterator iter = mSelectedObjects->begin(); @@ -4861,7 +4869,11 @@ void LLSelectMgr::renderSilhouettes(BOOL for_hud) { continue; } - if(objectp->getID() == inspect_item_id) + if (objectp->getID() == focus_item_id) + { + node->renderOneSilhouette(gFocusMgr.getFocusColor()); + } + else if(objectp->getID() == inspect_item_id) { node->renderOneSilhouette(sHighlightInspectColor); } @@ -4943,6 +4955,16 @@ void LLSelectMgr::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_ // Utility classes // LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow) +: mObject(object), + mIndividualSelection(FALSE), + mTransient(FALSE), + mValid(FALSE), + mPermissions(new LLPermissions()), + mInventorySerial(0), + mSilhouetteExists(FALSE), + mDuplicated(FALSE), + mTESelectMask(0), + mLastTESelected(0) { mObject = object; selectAllTEs(FALSE); @@ -4964,11 +4986,7 @@ LLSelectNode::LLSelectNode(LLViewerObject* object, BOOL glow) LLSelectNode::LLSelectNode(const LLSelectNode& nodep) { - S32 i; - for (i = 0; i < SELECT_MAX_TES; i++) - { - mTESelected[i] = nodep.mTESelected[i]; - } + mTESelectMask = nodep.mTESelectMask; mLastTESelected = nodep.mLastTESelected; mIndividualSelection = nodep.mIndividualSelection; @@ -5021,10 +5039,7 @@ LLSelectNode::~LLSelectNode() void LLSelectNode::selectAllTEs(BOOL b) { - for (S32 i = 0; i < SELECT_MAX_TES; i++) - { - mTESelected[i] = b; - } + mTESelectMask = b ? 0xFFFFFFFF : 0x0; mLastTESelected = 0; } @@ -5034,7 +5049,7 @@ void LLSelectNode::selectTE(S32 te_index, BOOL selected) { return; } - mTESelected[te_index] = selected; + mTESelectMask |= 0x1 << te_index; mLastTESelected = te_index; } @@ -5044,7 +5059,7 @@ BOOL LLSelectNode::isTESelected(S32 te_index) { return FALSE; } - return mTESelected[te_index]; + return (mTESelectMask & (0x1 << te_index)) != 0; } S32 LLSelectNode::getLastSelectedTE() diff --git a/indra/newview/llselectmgr.h b/indra/newview/llselectmgr.h index cfc2b702fc..c41a86e355 100644 --- a/indra/newview/llselectmgr.h +++ b/indra/newview/llselectmgr.h @@ -139,6 +139,7 @@ public: void selectTE(S32 te_index, BOOL selected); BOOL isTESelected(S32 te_index); S32 getLastSelectedTE(); + S32 getTESelectMask() { return mTESelectMask; } void renderOneSilhouette(const LLColor4 &color); void setTransient(BOOL transient) { mTransient = transient; } BOOL isTransient() { return mTransient; } @@ -191,7 +192,7 @@ public: protected: LLPointer<LLViewerObject> mObject; - BOOL mTESelected[SELECT_MAX_TES]; + S32 mTESelectMask; S32 mLastTESelected; }; @@ -409,7 +410,7 @@ public: // converts all objects currently highlighted to a selection, and returns it LLObjectSelectionHandle selectHighlightedObjects(); - LLObjectSelectionHandle setHoverObject(LLViewerObject *objectp); + LLObjectSelectionHandle setHoverObject(LLViewerObject *objectp, S32 face = -1); void highlightObjectOnly(LLViewerObject *objectp); void highlightObjectAndFamily(LLViewerObject *objectp); @@ -649,6 +650,7 @@ private: ESelectType getSelectTypeForObject(LLViewerObject* object); void addAsFamily(std::vector<LLViewerObject*>& objects, BOOL add_to_end = FALSE); void generateSilhouette(LLSelectNode *nodep, const LLVector3& view_point); + void updateSelectionSilhouette(LLObjectSelectionHandle object_handle, S32& num_sils_genned, std::vector<LLViewerObject*>& changed_objects); // Send one message to each region containing an object on selection list. void sendListToRegions( const std::string& message_name, void (*pack_header)(void *user_data), @@ -656,6 +658,7 @@ private: void *user_data, ESendType send_type); + static void packAgentID( void *); static void packAgentAndSessionID(void* user_data); static void packAgentAndGroupID(void* user_data); diff --git a/indra/newview/llsidetray.cpp b/indra/newview/llsidetray.cpp index 438b1b558f..5e5608460c 100644 --- a/indra/newview/llsidetray.cpp +++ b/indra/newview/llsidetray.cpp @@ -34,6 +34,7 @@ #include "lltextbox.h" +#include "llbottomtray.h" #include "llsidetray.h" #include "llviewerwindow.h" #include "llaccordionctrl.h" @@ -109,12 +110,15 @@ bool LLSideTray::instanceCreated () } LLSideTrayTab::LLSideTrayTab(const Params& params):mMainPanel(0) - { mImagePath = params.image_path; mTabTitle = params.tab_title; mDescription = params.description; + + // Necessary for focus movement among child controls + setFocusRoot(TRUE); } + LLSideTrayTab::~LLSideTrayTab() { } @@ -152,9 +156,12 @@ static const S32 splitter_margin = 1; //virtual void LLSideTrayTab::arrange(S32 width, S32 height ) { + if(!mMainPanel) + return; + S32 offset = 0; - LLView* title_panel = getChildView(TAB_PANEL_CAPTION_NAME, true, false); + LLView* title_panel = findChildView(TAB_PANEL_CAPTION_NAME, true); if(title_panel) { @@ -177,7 +184,7 @@ void LLSideTrayTab::reshape (S32 width, S32 height, BOOL called_from_parent ) return; S32 offset = 0; - LLView* title_panel = getChildView(TAB_PANEL_CAPTION_NAME, true, false); + LLView* title_panel = findChildView(TAB_PANEL_CAPTION_NAME, true); if(title_panel) { @@ -608,7 +615,7 @@ LLPanel* LLSideTray::showPanel (const std::string& panel_name, const LLSD& para child_vector_const_iter_t child_it; for ( child_it = mTabs.begin(); child_it != mTabs.end(); ++child_it) { - LLView* view = (*child_it)->getChildView(panel_name,true,false); + LLView* view = (*child_it)->findChildView(panel_name,true); if(view) { onTabButtonClick((*child_it)->getName()); @@ -647,7 +654,7 @@ void LLSideTray::setPanelRect () if(!mCollapsed) panel_width+=mMaxBarWidth; - S32 panel_height = parent_rect.getHeight()-fake_top_offset; + S32 panel_height = parent_rect.getHeight() - fake_top_offset - LLBottomTray::getInstance()->getRect().getHeight(); LLRect panel_rect; panel_rect.setLeftTopAndSize( parent_rect.mRight-panel_width, parent_rect.mTop-fake_top_offset, panel_width, panel_height); setRect(panel_rect); diff --git a/indra/newview/llsidetraypanelcontainer.cpp b/indra/newview/llsidetraypanelcontainer.cpp index 21061a802a..3024492ab9 100644 --- a/indra/newview/llsidetraypanelcontainer.cpp +++ b/indra/newview/llsidetraypanelcontainer.cpp @@ -85,5 +85,9 @@ BOOL LLSideTrayPanelContainer::handleKeyHere(KEY key, MASK mask) { // No key press handling code for Panel Container - this disables // Tab Container's Alt + Left/Right Button tab switching. - return TRUE; + + // Let default handler process key presses, don't simply return TRUE or FALSE + // as this may brake some functionality as it did with Copy/Paste for + // text_editor (ticket EXT-642). + return LLPanel::handleKeyHere(key, mask); } diff --git a/indra/newview/llsky.cpp b/indra/newview/llsky.cpp index ac7e865de0..a49b07c5d9 100644 --- a/indra/newview/llsky.cpp +++ b/indra/newview/llsky.cpp @@ -52,7 +52,6 @@ #include "llviewerobject.h" #include "llviewercamera.h" #include "pipeline.h" -#include "llagent.h" #include "lldrawpool.h" #include "llvosky.h" diff --git a/indra/newview/llslurl.cpp b/indra/newview/llslurl.cpp index ffadeeddf2..836fe9729d 100644 --- a/indra/newview/llslurl.cpp +++ b/indra/newview/llslurl.cpp @@ -113,9 +113,33 @@ std::string LLSLURL::buildUnescapedSLURL(const std::string& regionname, S32 x, S } // static +std::string LLSLURL::buildSLURLfromPosGlobal(const std::string& regionname, + const LLVector3d& global_pos, + bool escaped /*= true*/) +{ + S32 x, y, z; + globalPosToXYZ(global_pos, x, y, z); + if(escaped) + { + return buildSLURL(regionname, x, y, z); + } + else + { + return buildUnescapedSLURL(regionname, x, y, z); + } +} + +// static bool LLSLURL::matchPrefix(const std::string& url, const std::string& prefix) { std::string test_prefix = url.substr(0, prefix.length()); LLStringUtil::toLower(test_prefix); return test_prefix == prefix; } + +void LLSLURL::globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z) +{ + x = llround((F32)fmod(pos.mdV[VX], (F64)REGION_WIDTH_METERS)); + y = llround((F32)fmod(pos.mdV[VY], (F64)REGION_WIDTH_METERS)); + z = llround((F32)pos.mdV[VZ]); +} diff --git a/indra/newview/llslurl.h b/indra/newview/llslurl.h index 5c9fea3e96..8af2bdfb83 100644 --- a/indra/newview/llslurl.h +++ b/indra/newview/llslurl.h @@ -79,12 +79,25 @@ public: static std::string buildUnescapedSLURL(const std::string& regionname, S32 x, S32 y, S32 z); /** + * builds SLURL from global position. Returns escaped or unescaped url. + * Returns escaped url by default. + */ + static std::string buildSLURLfromPosGlobal(const std::string& regionname, + const LLVector3d& global_pos, + bool escaped = true); + /** * Strip protocol part from the URL. */ static std::string stripProtocol(const std::string& url); + /** + * Convert global position to X, Y Z + */ + static void globalPosToXYZ(const LLVector3d& pos, S32& x, S32& y, S32& z); + private: static bool matchPrefix(const std::string& url, const std::string& prefix); + }; #endif diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index c4364ed6ca..dea9af0657 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -41,7 +41,6 @@ #include "llviewercamera.h" #include "llface.h" #include "llviewercontrol.h" -#include "llagent.h" #include "llviewerregion.h" #include "llcamera.h" #include "pipeline.h" @@ -49,6 +48,9 @@ #include "lloctree.h" #include "llvoavatar.h" +static LLFastTimer::DeclareTimer FTM_FRUSTUM_CULL("Frustum Culling"); +static LLFastTimer::DeclareTimer FTM_CULL_REBOUND("Cull Rebound"); + const F32 SG_OCCLUSION_FUDGE = 0.25f; #define SG_DISCARD_TOLERANCE 0.01f @@ -570,6 +572,8 @@ void LLSpatialGroup::rebuildMesh() } } +static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt"); + void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group) { if (!gPipeline.hasRenderType(mDrawableType)) @@ -588,7 +592,7 @@ void LLSpatialPartition::rebuildGeom(LLSpatialGroup* group) return; } - LLFastTimer ftm(LLFastTimer::FTM_REBUILD_VBO); + LLFastTimer ftm(FTM_REBUILD_VBO); group->clearDrawMap(); @@ -1256,6 +1260,7 @@ BOOL LLSpatialGroup::rebound() return TRUE; } +static LLFastTimer::DeclareTimer FTM_OCCLUSION_READBACK("Readback Occlusion"); void LLSpatialGroup::checkOcclusion() { if (LLPipeline::sUseOcclusion > 1) @@ -1267,7 +1272,7 @@ void LLSpatialGroup::checkOcclusion() } else if (isState(QUERY_PENDING)) { //otherwise, if a query is pending, read it back - LLFastTimer t(LLFastTimer::FTM_OCCLUSION_READBACK); + LLFastTimer t(FTM_OCCLUSION_READBACK); GLuint res = 1; if (!isState(DISCARD_QUERY) && mOcclusionQuery) { @@ -1312,7 +1317,7 @@ void LLSpatialGroup::doOcclusion(LLCamera* camera) else { { - LLFastTimer t(LLFastTimer::FTM_RENDER_OCCLUSION); + LLFastTimer t(FTM_RENDER_OCCLUSION); if (!mOcclusionQuery) { @@ -1873,7 +1878,7 @@ S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* result { BOOL temp = sFreezeState; sFreezeState = FALSE; - LLFastTimer ftm(LLFastTimer::FTM_CULL_REBOUND); + LLFastTimer ftm(FTM_CULL_REBOUND); LLSpatialGroup* group = (LLSpatialGroup*) mOctree->getListener(0); group->rebound(); sFreezeState = temp; @@ -1891,19 +1896,19 @@ S32 LLSpatialPartition::cull(LLCamera &camera, std::vector<LLDrawable *>* result } else if (LLPipeline::sShadowRender) { - LLFastTimer ftm(LLFastTimer::FTM_FRUSTUM_CULL); + LLFastTimer ftm(FTM_FRUSTUM_CULL); LLOctreeCullShadow culler(&camera); culler.traverse(mOctree); } else if (mInfiniteFarClip || !LLPipeline::sUseFarClip) { - LLFastTimer ftm(LLFastTimer::FTM_FRUSTUM_CULL); + LLFastTimer ftm(FTM_FRUSTUM_CULL); LLOctreeCullNoFarClip culler(&camera); culler.traverse(mOctree); } else { - LLFastTimer ftm(LLFastTimer::FTM_FRUSTUM_CULL); + LLFastTimer ftm(FTM_FRUSTUM_CULL); LLOctreeCull culler(&camera); culler.traverse(mOctree); } diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 4afb0b06c8..8859f1ffb5 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -40,14 +40,15 @@ # include <sys/stat.h> // mkdir() #endif -#include "audioengine.h" +#include "llviewermedia_streamingaudio.h" +#include "llaudioengine.h" #ifdef LL_FMOD -# include "audioengine_fmod.h" +# include "llaudioengine_fmod.h" #endif #ifdef LL_OPENAL -#include "audioengine_openal.h" +#include "llaudioengine_openal.h" #endif #include "llares.h" @@ -188,10 +189,6 @@ #include "lllogin.h" #include "llevents.h" -#if LL_LIBXUL_ENABLED -#include "llmozlib.h" -#endif // LL_LIBXUL_ENABLED - #if LL_WINDOWS #include "llwindebug.h" #include "lldxhardware.h" @@ -317,6 +314,59 @@ void update_texture_fetch() gTextureList.updateImages(0.10f); } +//Copies landmarks from the "Library" to "My Favorites" +void populate_favorites_bar() +{ + //*TODO consider extending LLInventoryModel::findCategoryUUIDForType(...) to support both root's + LLInventoryModel::cat_array_t* lib_cats = NULL; + LLInventoryModel::item_array_t* lib_items = NULL; + gInventory.getDirectDescendentsOf(gInventory.getLibraryRootFolderID(), lib_cats, lib_items); + if (!lib_cats) return; + + LLUUID lib_landmarks(LLUUID::null); + S32 count = lib_cats->count(); + for(S32 i = 0; i < count; ++i) + { + if(lib_cats->get(i)->getPreferredType() == LLAssetType::AT_LANDMARK) + { + lib_landmarks = lib_cats->get(i)->getUUID(); + break; + } + } + if (lib_landmarks.isNull()) + { + llerror("Library inventory is missing Landmarks", 0); + return; + } + + LLInventoryModel::cat_array_t* lm_cats = NULL; + LLInventoryModel::item_array_t* lm_items = NULL; + gInventory.getDirectDescendentsOf(lib_landmarks, lm_cats, lm_items); + if (!lm_items) return; + + LLUUID favorites_id = gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE); + if (favorites_id.isNull()) + { + llerror("My Inventory is missing My Favorites", 0); + return; + } + + S32 lm_count = lm_items->count(); + for (S32 i = 0; i < lm_count; ++i) + { + LLInventoryItem* item = lm_items->get(i); + if (item->getUUID().isNull()) continue; + + copy_inventory_item(gAgent.getID(), + item->getPermissions().getOwner(), + item->getUUID(), + favorites_id, + std::string(), + LLPointer<LLInventoryCallback>(NULL)); + } +} + + // Returns false to skip other idle processing. Should only return // true when all initialization done. bool idle_startup() @@ -651,6 +701,16 @@ bool idle_startup() delete gAudiop; gAudiop = NULL; } + + if (gAudiop) + { + // if the audio engine hasn't set up its own preferred handler for streaming audio then set up the generic streaming audio implementation which uses media plugins + if (NULL == gAudiop->getStreamingAudioImpl()) + { + LL_INFOS("AppInit") << "Using media plugins to render streaming audio" << LL_ENDL; + gAudiop->setStreamingAudioImpl(new LLStreamingAudio_MediaPlugins()); + } + } } } @@ -735,10 +795,7 @@ bool idle_startup() std::string msg = LLTrans::getString("LoginInitializingBrowser"); set_startup_status(0.03f, msg.c_str(), gAgent.mMOTD.c_str()); display_startup(); -#if !defined(LL_WINDOWS) || !defined(LL_DEBUG) - // This generates an error in debug mode on Windows - LLViewerMedia::initBrowser(); -#endif + // LLViewerMedia::initBrowser(); LLStartUp::setStartupState( STATE_LOGIN_SHOW ); return FALSE; } @@ -912,11 +969,10 @@ bool idle_startup() } - //For HTML parsing in text boxes. - LLTextEditor::setLinkColor( LLUIColorTable::instance().getColor("HTMLLinkColor") ); - // Load URL History File LLURLHistory::loadFile("url_history.xml"); + // Load location history + LLLocationHistory::getInstance()->load(); //------------------------------------------------- // Handle startup progress screen @@ -975,6 +1031,7 @@ bool idle_startup() gDebugInfo["GridName"] = LLViewerLogin::getInstance()->getGridLabel(); // Update progress status and the display loop. + requested_options.push_back("adult_compliant"); auth_desc = LLTrans::getString("LoginInProgress"); set_startup_status(progress, auth_desc, auth_message); progress += 0.02f; @@ -1587,7 +1644,7 @@ bool idle_startup() } - //all categories loaded. lets create "My Favourites" category + //all categories loaded. lets create "My Favorites" category gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE,true); gInventory.buildParentChildMap(); @@ -1622,6 +1679,12 @@ bool idle_startup() llinfos << "Creating Inventory Views" << llendl; LLFloaterReg::getInstance("inventory"); + //default initial content for Favorites Bar + if (gAgent.isFirstLogin()) + { + populate_favorites_bar(); + } + LLStartUp::setStartupState( STATE_MISC ); return FALSE; } @@ -1998,7 +2061,7 @@ bool idle_startup() // reset timers now that we are running "logged in" logic LLFastTimer::reset(); - LLLocationHistory::getInstance()->load(); + return TRUE; } @@ -2686,7 +2749,7 @@ void LLStartUp::multimediaInit() set_startup_status(0.42f, msg.c_str(), gAgent.mMOTD.c_str()); display_startup(); - LLViewerMedia::initClass(); + // LLViewerMedia::initClass(); LLViewerParcelMedia::initClass(); } @@ -2705,7 +2768,7 @@ bool LLStartUp::dispatchURL() // ok, if we've gotten this far and have a startup URL if (!sSLURLCommand.empty()) { - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; const bool trusted_browser = false; LLURLDispatcher::dispatch(sSLURLCommand, web, trusted_browser); } @@ -2723,7 +2786,7 @@ bool LLStartUp::dispatchURL() || (dy*dy > SLOP*SLOP) ) { std::string url = LLURLSimString::getURL(); - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; const bool trusted_browser = false; LLURLDispatcher::dispatch(url, web, trusted_browser); } diff --git a/indra/newview/llstatusbar.cpp b/indra/newview/llstatusbar.cpp index 569e7b3397..9cabcf4680 100644 --- a/indra/newview/llstatusbar.cpp +++ b/indra/newview/llstatusbar.cpp @@ -43,7 +43,6 @@ #include "llfloaterchat.h" #include "llfloaterdirectory.h" // to spawn search #include "llfloaterlagmeter.h" -#include "llfloaterland.h" #include "llfloaterregioninfo.h" #include "llfloaterscriptdebug.h" #include "llhudicon.h" @@ -70,7 +69,6 @@ #include "llviewerparcelmgr.h" #include "llviewerthrottle.h" #include "lluictrlfactory.h" -#include "llvoiceclient.h" // for gVoiceClient #include "lltoolmgr.h" #include "llfocusmgr.h" @@ -112,12 +110,6 @@ const S32 TEXT_HEIGHT = 18; static void onClickBuyCurrency(void*); static void onClickHealth(void*); -static void onClickFly(void*); -static void onClickPush(void*); -static void onClickVoice(void*); -static void onClickBuild(void*); -static void onClickScripts(void*); -static void onClickBuyLand(void*); static void onClickScriptDebug(void*); std::vector<std::string> LLStatusBar::sDays; @@ -160,19 +152,6 @@ LLStatusBar::LLStatusBar(const LLRect& rect) childSetAction("scriptout", onClickScriptDebug, this); childSetAction("health", onClickHealth, this); - childSetAction("no_fly", onClickFly, this); - childSetAction("buyland", onClickBuyLand, this ); - childSetAction("no_build", onClickBuild, this ); - childSetAction("no_scripts", onClickScripts, this ); - childSetAction("restrictpush", onClickPush, this ); - childSetAction("status_no_voice", onClickVoice, this ); - - childSetCommitCallback("search_editor", onCommitSearch, this); - childSetAction("search_btn", onClickSearch, this); - - childSetVisible("search_editor", gSavedSettings.getBOOL("ShowSearchBar")); - childSetVisible("search_btn", gSavedSettings.getBOOL("ShowSearchBar")); - childSetVisible("menubar_search_bevel_bg", gSavedSettings.getBOOL("ShowSearchBar")); // Adding Net Stat Graph S32 x = getRect().getWidth() - 2; @@ -265,7 +244,7 @@ BOOL LLStatusBar::postBuild() mHideNavbarContextMenu = LLUICtrlFactory::getInstance()->createFromFile<LLMenuGL>("menu_hide_navbar.xml", gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance()); gMenuHolder->addChild(mHideNavbarContextMenu); - gMenuBarView->setRightClickedCallback(boost::bind(&LLStatusBar::onMainMenuRightClicked, this, _1, _2, _3, _4)); + gMenuBarView->setRightMouseDownCallback(boost::bind(&LLStatusBar::onMainMenuRightClicked, this, _1, _2, _3, _4)); return TRUE; } @@ -331,8 +310,8 @@ void LLStatusBar::refresh() childSetVisible("scriptout", false); } - if ((region && region->getAllowDamage()) || - (parcel && parcel->getAllowDamage()) ) + if (gAgent.getCameraMode() == CAMERA_MODE_MOUSELOOK && + ((region && region->getAllowDamage()) || (parcel && parcel->getAllowDamage()))) { // set visibility based on flashing if( mHealthTimer->hasExpired() ) @@ -364,116 +343,6 @@ void LLStatusBar::refresh() mTextHealth->setVisible(FALSE); } - if ((region && region->getBlockFly()) || - (parcel && !parcel->getAllowFly()) ) - { - // No Fly Zone - childGetRect( "no_fly", buttonRect ); - childSetVisible( "no_fly", true ); - r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "no_fly", r ); - x += buttonRect.getWidth(); - } - else - { - // Fly Zone - childSetVisible("no_fly", false); - } - - BOOL no_build = parcel && !parcel->getAllowModify(); - if (no_build) - { - childSetVisible("no_build", TRUE); - childGetRect( "no_build", buttonRect ); - // No Build Zone - r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "no_build", r ); - x += buttonRect.getWidth(); - } - else - { - childSetVisible("no_build", FALSE); - } - - BOOL no_scripts = FALSE; - if((region - && ((region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS) - || (region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS))) - || (parcel && !parcel->getAllowOtherScripts())) - { - no_scripts = TRUE; - } - if (no_scripts) - { - // No scripts - childSetVisible("no_scripts", TRUE); - childGetRect( "no_scripts", buttonRect ); - r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "no_scripts", r ); - x += buttonRect.getWidth(); - } - else - { - // Yes scripts - childSetVisible("no_scripts", FALSE); - } - - BOOL no_region_push = (region && region->getRestrictPushObject()); - BOOL no_push = no_region_push || (parcel && parcel->getRestrictPushObject()); - if (no_push) - { - childSetVisible("restrictpush", TRUE); - childGetRect( "restrictpush", buttonRect ); - r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "restrictpush", r ); - x += buttonRect.getWidth(); - } - else - { - childSetVisible("restrictpush", FALSE); - } - - BOOL have_voice = parcel && parcel->getParcelFlagAllowVoice(); - if (have_voice) - { - childSetVisible("status_no_voice", FALSE); - } - else - { - childSetVisible("status_no_voice", TRUE); - childGetRect( "status_no_voice", buttonRect ); - r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "status_no_voice", r ); - x += buttonRect.getWidth(); - } - - BOOL canBuyLand = parcel - && !parcel->isPublic() - && LLViewerParcelMgr::getInstance()->canAgentBuyParcel(parcel, false); - childSetVisible("buyland", canBuyLand); - if (canBuyLand) - { - //HACK: layout tweak until this is all xml - x += 9; - childGetRect( "buyland", buttonRect ); - r.setOriginAndSize( x, y, buttonRect.getWidth(), buttonRect.getHeight()); - childSetRect( "buyland", r ); - x += buttonRect.getWidth(); - } - - bool search_visible = gSavedSettings.getBOOL("ShowSearchBar"); - - // Set search bar visibility - - if (gAgent.getCameraMode() != CAMERA_MODE_MOUSELOOK) - { - // don't monkey with search visibility in mouselook - it will be set - // with setVisibleForMouselook() below - childSetVisible("search_editor", search_visible); - childSetVisible("search_btn", search_visible); - childSetVisible("menubar_search_bevel_bg", search_visible); - } - mSGBandwidth->setVisible(net_stats_visible); mSGPacketLoss->setVisible(net_stats_visible); childSetEnabled("stat_btn", net_stats_visible); @@ -483,9 +352,6 @@ void LLStatusBar::setVisibleForMouselook(bool visible) { mTextTime->setVisible(visible); mBtnBuyCurrency->setVisible(visible); - childSetVisible("search_editor", visible); - childSetVisible("search_btn", visible); - childSetVisible("menubar_search_bevel_bg", visible); mSGBandwidth->setVisible(visible); mSGPacketLoss->setVisible(visible); setBackgroundVisible(visible); @@ -635,49 +501,6 @@ static void onClickScriptDebug(void*) LLFloaterScriptDebug::show(LLUUID::null); } -static void onClickFly(void* ) -{ - LLNotifications::instance().add("NoFly"); -} - -static void onClickPush(void* ) -{ - LLNotifications::instance().add("PushRestricted"); -} - -static void onClickVoice(void* ) -{ - LLNotifications::instance().add("NoVoice"); -} - -static void onClickBuild(void*) -{ - LLNotifications::instance().add("NoBuild"); -} - -static void onClickScripts(void*) -{ - LLViewerRegion* region = gAgent.getRegion(); - if(region && region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS) - { - LLNotifications::instance().add("ScriptsStopped"); - } - else if(region && region->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS) - { - LLNotifications::instance().add("ScriptsNotRunning"); - } - else - { - LLNotifications::instance().add("NoOutsideScripts"); - } -} - -static void onClickBuyLand(void*) -{ - LLViewerParcelMgr::getInstance()->selectParcelAt(gAgent.getPositionGlobal()); - LLViewerParcelMgr::getInstance()->startBuyLand(); -} - // sets the static variables necessary for the date void LLStatusBar::setupDate() { @@ -784,21 +607,6 @@ void LLStatusBar::onMainMenuRightClicked(LLUICtrl* ctrl, S32 x, S32 y, MASK mask } // static -void LLStatusBar::onCommitSearch(LLUICtrl*, void* data) -{ - // committing is the same as clicking "search" - onClickSearch(data); -} - -// static -void LLStatusBar::onClickSearch(void* data) -{ - LLStatusBar* self = (LLStatusBar*)data; - std::string search_text = self->childGetText("search_editor"); - LLFloaterReg::showInstance("search", LLSD().insert("panel", "all").insert("id", LLSD(search_text))); -} - -// static void LLStatusBar::onClickStatGraph(void* data) { LLFloaterReg::showInstance("lagmeter"); @@ -817,7 +625,7 @@ class LLBalanceHandler : public LLCommandHandler public: // Requires "trusted" browser/URL source LLBalanceHandler() : LLCommandHandler("balance", true) { } - bool handle(const LLSD& tokens, const LLSD& query_map, LLWebBrowserCtrl* web) + bool handle(const LLSD& tokens, const LLSD& query_map, LLMediaCtrl* web) { if (tokens.size() == 1 && tokens[0].asString() == "request") diff --git a/indra/newview/llstylemap.cpp b/indra/newview/llstylemap.cpp index a422db1cc1..fc125dcf9c 100644 --- a/indra/newview/llstylemap.cpp +++ b/indra/newview/llstylemap.cpp @@ -38,83 +38,53 @@ #include "llviewercontrol.h" #include "llagent.h" -LLStyleMap::LLStyleMap() -{ -} - -LLStyleMap::~LLStyleMap() -{ -} - -LLStyleMap &LLStyleMap::instance() -{ - static LLStyleMap style_map; - return style_map; -} - -// This is similar to the [] accessor except that if the entry doesn't already exist, -// then this will create the entry. -const LLStyleSP &LLStyleMap::lookupAgent(const LLUUID &source) +const LLStyle::Params &LLStyleMap::lookupAgent(const LLUUID &source) { // Find this style in the map or add it if not. This map holds links to residents' profiles. - if (find(source) == end()) + if (mMap.find(source) == mMap.end()) { - LLStyleSP style(new LLStyle); - style->setVisible(true); - style->setFontName(LLStringUtil::null); + LLStyle::Params style_params; if (source != LLUUID::null && source != gAgent.getID() ) { - style->setColor(LLUIColorTable::instance().getColor("HTMLLinkColor")); - std::string link = llformat("secondlife:///app/agent/%s/about",source.asString().c_str()); - style->setLinkHREF(link); + style_params.color.control = "HTMLLinkColor"; + style_params.link_href = llformat("secondlife:///app/agent/%s/about",source.asString().c_str()); } else { // Make the resident's own name white and don't make the name clickable. - style->setColor(LLColor4::white); + style_params.color = LLColor4::white; } - (*this)[source] = style; + + mMap[source] = LLStyle::Params(); } - return (*this)[source]; + return mMap[source]; } // This is similar to lookupAgent for any generic URL encoded style. -const LLStyleSP &LLStyleMap::lookup(const LLUUID& id, const std::string& link) +const LLStyle::Params &LLStyleMap::lookup(const LLUUID& id, const std::string& link) { // Find this style in the map or add it if not. - iterator iter = find(id); - if (iter == end()) + style_map_t::iterator iter = mMap.find(id); + if (iter == mMap.end()) { - LLStyleSP style(new LLStyle); - style->setVisible(true); - style->setFontName(LLStringUtil::null); + LLStyle::Params style_params; + if (id != LLUUID::null && !link.empty()) { - style->setColor(LLUIColorTable::instance().getColor("HTMLLinkColor")); - style->setLinkHREF(link); + style_params.color.control = "HTMLLinkColor"; + style_params.link_href = link; } else - style->setColor(LLColor4::white); - (*this)[id] = style; + { + style_params.color = LLColor4::white; + } + mMap[id] = style_params; } else { - LLStyleSP style = (*iter).second; - if ( style->getLinkHREF() != link ) - { - style->setLinkHREF(link); - } + iter->second.link_href = link; } - return (*this)[id]; + return mMap[id]; } -void LLStyleMap::update() -{ - for (style_map_t::iterator iter = begin(); iter != end(); ++iter) - { - LLStyleSP &style = iter->second; - // Update the link color in case it has been changed. - style->setColor(LLUIColorTable::instance().getColor("HTMLLinkColor")); - } -} diff --git a/indra/newview/llstylemap.h b/indra/newview/llstylemap.h index 254509ea1d..d29ff1ae28 100644 --- a/indra/newview/llstylemap.h +++ b/indra/newview/llstylemap.h @@ -36,24 +36,22 @@ #include "llstyle.h" #include "lluuid.h" +#include "llsingleton.h" // Lightweight class for holding and managing mappings between UUIDs and links. // Used (for example) to create clickable name links off of IM chat. -typedef std::map<LLUUID, LLStyleSP> style_map_t; +typedef std::map<LLUUID, LLStyle::Params> style_map_t; -class LLStyleMap : public style_map_t +class LLStyleMap : public LLSingleton<LLStyleMap> { public: - LLStyleMap(); - ~LLStyleMap(); // Just like the [] accessor but it will add the entry in if it doesn't exist. - const LLStyleSP &lookupAgent(const LLUUID &source); - const LLStyleSP &lookup(const LLUUID &source, const std::string& link); - static LLStyleMap &instance(); + const LLStyle::Params &lookupAgent(const LLUUID &source); + const LLStyle::Params &lookup(const LLUUID &source, const std::string& link); - // Forces refresh of the entries, call when something changes (e.g. link color). - void update(); +private: + style_map_t mMap; }; #endif // LL_LLSTYLE_MAP_H diff --git a/indra/newview/llsyswellitem.cpp b/indra/newview/llsyswellitem.cpp index 5ed8a8b604..7d9ebc7208 100644 --- a/indra/newview/llsyswellitem.cpp +++ b/indra/newview/llsyswellitem.cpp @@ -37,6 +37,7 @@ #include "llwindow.h" #include "v4color.h" +#include "lluicolortable.h" //--------------------------------------------------------------------------------- LLSysWellItem::LLSysWellItem(const Params& p) : LLScrollingPanel(p), @@ -76,7 +77,10 @@ void LLSysWellItem::onClickCloseBtn() //--------------------------------------------------------------------------------- void LLSysWellItem::updatePanel(BOOL allow_modify) { - //nothing to do here + S32 parent_width = getParent()->getRect().getWidth(); + S32 panel_height = getRect().getHeight(); + + reshape(parent_width, panel_height, TRUE); } //--------------------------------------------------------------------------------- @@ -91,13 +95,13 @@ BOOL LLSysWellItem::handleMouseDown(S32 x, S32 y, MASK mask) //--------------------------------------------------------------------------------- void LLSysWellItem::onMouseEnter(S32 x, S32 y, MASK mask) { - setTransparentColor(LLColor4(0.3f, 0.3f, 0.3f, 1.0f)); + setTransparentColor(LLUIColorTable::instance().getColor( "SysWellItemSelected" )); } //--------------------------------------------------------------------------------- void LLSysWellItem::onMouseLeave(S32 x, S32 y, MASK mask) { - setTransparentColor(LLColor4(0.0f, 0.0f, 0.0f, 0.0f)); + setTransparentColor(LLUIColorTable::instance().getColor( "SysWellItemUnselected" )); } //--------------------------------------------------------------------------------- diff --git a/indra/newview/llsyswellwindow.cpp b/indra/newview/llsyswellwindow.cpp index c8eea5e7b4..2bbb5a2767 100644 --- a/indra/newview/llsyswellwindow.cpp +++ b/indra/newview/llsyswellwindow.cpp @@ -36,7 +36,7 @@ #include "llbottomtray.h" #include "llviewercontrol.h" - +#include "llviewerwindow.h" //--------------------------------------------------------------------------------- LLSysWellWindow::LLSysWellWindow(const LLSD& key) : LLFloater(LLSD()), @@ -44,19 +44,18 @@ LLSysWellWindow::LLSysWellWindow(const LLSD& key) : LLFloater(LLSD()), mScrollContainer(NULL), mNotificationList(NULL) { - // Ho to use: - // LLFloaterReg::showInstance("syswell_window"); - LLUICtrlFactory::getInstance()->buildFloater(this, "floater_sys_well.xml", NULL); } //--------------------------------------------------------------------------------- BOOL LLSysWellWindow::postBuild() { - mCloseBtn = getChild<LLButton>("close_btn"); mScrollContainer = getChild<LLScrollContainer>("notification_list_container"); mNotificationList = getChild<LLScrollingPanelList>("notification_list"); - mCloseBtn->setClickedCallback(boost::bind(&LLSysWellWindow::onClickCloseBtn,this)); + gViewerWindow->setOnBottomTrayWidthChanged(boost::bind(&LLSysWellWindow::adjustWindowPosition, this)); // *TODO: won't be necessary after docking is realized + mScrollContainer->setBorderVisible(FALSE); + + mDockTongue = LLUI::getUIImage("windows/Flyout_Pointer.png"); return TRUE; } @@ -76,7 +75,7 @@ void LLSysWellWindow::addItem(LLSysWellItem::Params p) LLSysWellItem* new_item = new LLSysWellItem(p); mNotificationList->addPanel(dynamic_cast<LLScrollingPanel*>(new_item)); reshapeWindow(); - adjustWindowPosition(); + adjustWindowPosition(); // *TODO: won't be necessary after docking is realized new_item->setOnItemCloseCallback(boost::bind(&LLSysWellWindow::onItemClose, this, _1)); new_item->setOnItemClickCallback(boost::bind(&LLSysWellWindow::onItemClick, this, _1)); @@ -95,14 +94,12 @@ S32 LLSysWellWindow::findItemByID(const LLUUID& id) if(list.size() == 0) return -1; - LLScrollingPanelList::panel_list_t::const_iterator it = list.begin(); + LLScrollingPanelList::panel_list_t::const_iterator it; S32 index = 0; - while(it != list.end()) + for(it = list.begin(); it != list.end(); ++it, ++index) { if( dynamic_cast<LLSysWellItem*>(*it)->getID() == id ) break; - ++it; - ++index; } if(it == list.end()) @@ -123,7 +120,7 @@ void LLSysWellWindow::removeItemByID(const LLUUID& id) return; reshapeWindow(); - adjustWindowPosition(); + adjustWindowPosition(); // *TODO: won't be necessary after docking is realized // hide chiclet window if there are no items left S32 items_left = mNotificationList->getPanelList().size(); if(items_left == 0) @@ -134,7 +131,8 @@ void LLSysWellWindow::removeItemByID(const LLUUID& id) void LLSysWellWindow::onItemClick(LLSysWellItem* item) { LLUUID id = item->getID(); - mChannel->loadStoredToastByIDToChannel(id); + if(mChannel) + mChannel->loadStoredToastByIDToChannel(id); } //--------------------------------------------------------------------------------- @@ -142,13 +140,14 @@ void LLSysWellWindow::onItemClose(LLSysWellItem* item) { LLUUID id = item->getID(); removeItemByID(id); - mChannel->killToastByNotificationID(id); + if(mChannel) + mChannel->killToastByNotificationID(id); } //--------------------------------------------------------------------------------- -void LLSysWellWindow::onClickCloseBtn() +void LLSysWellWindow::toggleWindow() { - setVisible(false); + setVisible(!getVisible()); } //--------------------------------------------------------------------------------- @@ -157,15 +156,20 @@ void LLSysWellWindow::setVisible(BOOL visible) // on Show adjust position of SysWell chiclet's window if(visible) { - mChannel->removeAndStoreAllVisibleToasts(); - adjustWindowPosition(); + if(mChannel) + mChannel->removeAndStoreAllVisibleToasts(); + + adjustWindowPosition(); // *TODO: won't be necessary after docking is realized } + if(mChannel) + mChannel->setShowToasts(!visible); + LLFloater::setVisible(visible); } //--------------------------------------------------------------------------------- -void LLSysWellWindow::adjustWindowPosition() +void LLSysWellWindow::adjustWindowPosition() // *TODO: won't be necessary after docking is realized { const S32 WINDOW_MARGIN = 5; @@ -176,32 +180,31 @@ void LLSysWellWindow::adjustWindowPosition() //--------------------------------------------------------------------------------- void LLSysWellWindow::reshapeWindow() { - // Get scrollbar size + // Get size for scrollbar and floater's header const LLUICachedControl<S32> SCROLLBAR_SIZE("UIScrollbarSize", 0); + const LLUICachedControl<S32> HEADER_SIZE("UIFloaterHeaderSize", 0); // Get item list const LLScrollingPanelList::panel_list_t list = mNotificationList->getPanelList(); - // window's size constants - const S32 WINDOW_HEADER_HEIGHT = 30; - const S32 MAX_WINDOW_HEIGHT = 200; - const S32 MIN_WINDOW_WIDTH = 320; - - // Get height and border's width for a scrolling panel list - S32 list_height = mNotificationList->getRect().getHeight(); - S32 list_border_width = mScrollContainer->getBorderWidth() * 2; + // Get height for a scrolling panel list + S32 list_height = mNotificationList->getRect().getHeight(); // Check that the floater doesn't exceed its parent view limits after reshape - S32 new_height = list_height + WINDOW_HEADER_HEIGHT + list_border_width; + S32 new_height = list_height + LLScrollingPanelList::GAP_BETWEEN_PANELS + HEADER_SIZE; if(new_height > MAX_WINDOW_HEIGHT) { - reshape(MIN_WINDOW_WIDTH + SCROLLBAR_SIZE, MAX_WINDOW_HEIGHT, FALSE); + reshape(MIN_WINDOW_WIDTH, MAX_WINDOW_HEIGHT, FALSE); + mNotificationList->reshape(MIN_PANELLIST_WIDTH - SCROLLBAR_SIZE, list_height, TRUE); } else { reshape(MIN_WINDOW_WIDTH, new_height, FALSE); + mNotificationList->reshape(MIN_PANELLIST_WIDTH, list_height, TRUE); } + + mNotificationList->updatePanels(TRUE); } //--------------------------------------------------------------------------------- diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 9554f3cb82..7a107af0d1 100644 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -46,8 +46,6 @@ class LLSysWellWindow : public LLFloater { - friend class LLFloaterReg; - public: LLSysWellWindow(const LLSD& key); ~LLSysWellWindow(); @@ -65,20 +63,25 @@ public: // Operating with outfit virtual void setVisible(BOOL visible); void adjustWindowPosition(); + void toggleWindow(); // Handlers void onItemClick(LLSysWellItem* item); void onItemClose(LLSysWellItem* item); + // size constants for the window and for its elements + static const S32 MAX_WINDOW_HEIGHT = 200; + static const S32 MIN_WINDOW_WIDTH = 320; + static const S32 MIN_PANELLIST_WIDTH = 318; + private: - void onClickCloseBtn(); void reshapeWindow(); // pointer to a corresponding channel's instance LLNotificationsUI::LLScreenChannel* mChannel; - LLButton* mCloseBtn; + LLUIImagePtr mDockTongue; LLScrollContainer* mScrollContainer; LLScrollingPanelList* mNotificationList; }; diff --git a/indra/newview/llteleporthistory.cpp b/indra/newview/llteleporthistory.cpp index 9754568f56..5235dc9358 100644 --- a/indra/newview/llteleporthistory.cpp +++ b/indra/newview/llteleporthistory.cpp @@ -43,6 +43,7 @@ #include "llviewerparcelmgr.h" #include "llviewerregion.h" #include "llworldmap.h" +#include "llagentui.h" ////////////////////////////////////////////////////////////////////////////// // LLTeleportHistoryItem @@ -74,7 +75,7 @@ LLTeleportHistory::LLTeleportHistory(): mGotInitialUpdate(false) { mTeleportFinishedConn = LLViewerParcelMgr::getInstance()-> - setTeleportFinishedCallback(boost::bind(&LLTeleportHistory::updateCurrentLocation, this)); + setTeleportFinishedCallback(boost::bind(&LLTeleportHistory::updateCurrentLocation, this, _1)); mTeleportFailedConn = LLViewerParcelMgr::getInstance()-> setTeleportFailedCallback(boost::bind(&LLTeleportHistory::onTeleportFailed, this)); } @@ -118,7 +119,7 @@ void LLTeleportHistory::onTeleportFailed() } } -void LLTeleportHistory::updateCurrentLocation() +void LLTeleportHistory::updateCurrentLocation(const LLVector3d& new_pos) { if (mRequestedItem != -1) // teleport within the history in progress? { @@ -149,7 +150,7 @@ void LLTeleportHistory::updateCurrentLocation() return; } mItems[mCurrentItem].mTitle = getCurrentLocationTitle(); - mItems[mCurrentItem].mGlobalPos = gAgent.getPositionGlobal(); + mItems[mCurrentItem].mGlobalPos = new_pos; mItems[mCurrentItem].mRegionID = gAgent.getRegion()->getRegionID(); } @@ -184,10 +185,7 @@ void LLTeleportHistory::purgeItems() std::string LLTeleportHistory::getCurrentLocationTitle() { std::string location_name; - - if (!gAgent.buildLocationString(location_name, LLAgent::LOCATION_FORMAT_NORMAL)) - location_name = "Unknown"; - + if (!LLAgentUI::buildLocationString(location_name, LLAgent::LOCATION_FORMAT_NORMAL)) location_name = "Unknown"; return location_name; } @@ -201,6 +199,7 @@ void LLTeleportHistory::dump() const line << ((i == mCurrentItem) ? " * " : " "); line << i << ": " << mItems[i].mTitle; line << " REGION_ID: " << mItems[i].mRegionID; + line << ", pos: " << mItems[i].mGlobalPos; llinfos << line.str() << llendl; } } diff --git a/indra/newview/llteleporthistory.h b/indra/newview/llteleporthistory.h index 775b21e24c..060534635d 100644 --- a/indra/newview/llteleporthistory.h +++ b/indra/newview/llteleporthistory.h @@ -147,6 +147,10 @@ private: /** * Update current location. * + * @param new_pos Current agent global position. After local teleports we + * cannot rely on gAgent.getPositionGlobal(), + * so the new position gets passed explicitly. + * * Called when a teleport finishes. * Called via callback set on the LLViewerParcelMgr "teleport finished" signal. * @@ -158,7 +162,7 @@ private: * @see mRequestedItem * @see mGotInitialUpdate */ - void updateCurrentLocation(); + void updateCurrentLocation(const LLVector3d& new_pos); /** * Invokes the "history changed" callback(s). diff --git a/indra/newview/llteleporthistorystorage.cpp b/indra/newview/llteleporthistorystorage.cpp new file mode 100644 index 0000000000..b31edf1e5d --- /dev/null +++ b/indra/newview/llteleporthistorystorage.cpp @@ -0,0 +1,164 @@ +/** + * @file llteleporthistorystorage.cpp + * @brief Teleport history + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 "llviewerprecompiledheaders.h" + +#include "llteleporthistorystorage.h" + +#include "llsd.h" +#include "llsdserialize.h" +#include "lldir.h" + +static LLTeleportHistoryStorage tpstorage; + +LLTeleportHistoryPersistentItem::LLTeleportHistoryPersistentItem(const LLSD& val) +{ + mTitle = val["title"].asString(); + mGlobalPos.setValue(val["global_pos"]); + mDate = val["date"]; +} + +LLSD LLTeleportHistoryPersistentItem::toLLSD() const +{ + LLSD val; + + val["title"] = mTitle; + val["global_pos"] = mGlobalPos.getValue(); + val["date"] = mDate; + + return val; +} + +LLTeleportHistoryStorage::LLTeleportHistoryStorage() : + mFilename("teleport_history.txt") +{ +} + +LLTeleportHistoryStorage::~LLTeleportHistoryStorage() +{ +} + +void LLTeleportHistoryStorage::purgeItems() +{ + mItems.clear(); +} + +void LLTeleportHistoryStorage::addItem(const std::string title, const LLVector3d& global_pos) +{ + mItems.push_back(LLTeleportHistoryPersistentItem(title, global_pos)); +} + +void LLTeleportHistoryStorage::addItem(const std::string title, const LLVector3d& global_pos, const LLDate& date) +{ + mItems.push_back(LLTeleportHistoryPersistentItem(title, global_pos, date)); +} + +void LLTeleportHistoryStorage::removeItem(S32 idx) +{ + if (idx < 0 || idx >= (S32)mItems.size()) + return; + + mItems.erase (mItems.begin() + idx); +} + +void LLTeleportHistoryStorage::save() +{ + // build filename for each user + std::string resolvedFilename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, mFilename); + + // open the history file for writing + llofstream file (resolvedFilename); + if (!file.is_open()) + { + llwarns << "can't open teleport history file \"" << mFilename << "\" for writing" << llendl; + return; + } + + for (size_t i=0; i<mItems.size(); i++) + { + LLSD s_item = mItems[i].toLLSD(); + file << LLSDOStreamer<LLSDNotationFormatter>(s_item) << std::endl; + } + + file.close(); +} + +void LLTeleportHistoryStorage::load() +{ + // build filename for each user + std::string resolved_filename = gDirUtilp->getExpandedFilename(LL_PATH_PER_SL_ACCOUNT, mFilename); + + // open the history file for reading + llifstream file(resolved_filename); + if (!file.is_open()) + { + llwarns << "can't load teleport history from file \"" << mFilename << "\"" << llendl; + return; + } + + // remove current entries before we load over them + mItems.clear(); + + // the parser's destructor is protected so we cannot create in the stack. + LLPointer<LLSDParser> parser = new LLSDNotationParser(); + std::string line; + while (std::getline(file, line)) + { + LLSD s_item; + std::istringstream iss(line); + if (parser->parse(iss, s_item, line.length()) == LLSDParser::PARSE_FAILURE) + { + llinfos << "Parsing saved teleport history failed" << llendl; + break; + } + + mItems.push_back(s_item); + } + + file.close(); +} + +void LLTeleportHistoryStorage::dump() const +{ + llinfos << "Teleport history storage dump (" << mItems.size() << " items):" << llendl; + + for (size_t i=0; i<mItems.size(); i++) + { + std::stringstream line; + line << i << ": " << mItems[i].mTitle; + line << " global pos: " << mItems[i].mGlobalPos; + line << " date: " << mItems[i].mDate; + + llinfos << line.str() << llendl; + } +} + diff --git a/indra/newview/llteleporthistorystorage.h b/indra/newview/llteleporthistorystorage.h new file mode 100644 index 0000000000..fa836c0326 --- /dev/null +++ b/indra/newview/llteleporthistorystorage.h @@ -0,0 +1,108 @@ +/** + * @file llteleporthistorystorage.h + * @brief Saving/restoring of teleport history. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_LLTELEPORTHISTORYSTORAGE_H +#define LL_LLTELEPORTHISTORYSTORAGE_H + +#include <vector> + +#include "llsingleton.h" +#include "lldate.h" +#include "v3dmath.h" + +class LLSD; + +/** + * An item of the teleport history, stored in file + * + * Contains the location's global coordinates, title and date. + */ +class LLTeleportHistoryPersistentItem +{ +public: + LLTeleportHistoryPersistentItem() + {} + + LLTeleportHistoryPersistentItem(const std::string title, const LLVector3d& global_pos) + : mTitle(title), mGlobalPos(global_pos), mDate(LLDate::now()) + {} + + LLTeleportHistoryPersistentItem(const std::string title, const LLVector3d& global_pos, const LLDate& date) + : mTitle(title), mGlobalPos(global_pos), mDate(date) + {} + + LLTeleportHistoryPersistentItem(const LLSD& val); + LLSD toLLSD() const; + + std::string mTitle; + LLVector3d mGlobalPos; + LLDate mDate; +}; + +/** + * Persistent teleport history. + * + */ +class LLTeleportHistoryStorage: public LLSingleton<LLTeleportHistoryStorage> +{ + LOG_CLASS(LLTeleportHistoryStorage); + +public: + + typedef std::vector<LLTeleportHistoryPersistentItem> item_list_list_t; + + LLTeleportHistoryStorage(); + ~LLTeleportHistoryStorage(); + + /** + * @return history items. + */ + const item_list_list_t& getItems() const { return mItems; } + void purgeItems(); + + void addItem(const std::string title, const LLVector3d& global_pos); + void addItem(const std::string title, const LLVector3d& global_pos, const LLDate& date); + + void removeItem(S32 idx); + + void save(); + void load(); + + void dump() const; + +private: + + item_list_list_t mItems; + std::string mFilename; +}; + +#endif //LL_LLTELEPORTHISTORYSTORAGE_H diff --git a/indra/newview/lltoast.cpp b/indra/newview/lltoast.cpp index a67ef85f87..85814a98c9 100644 --- a/indra/newview/lltoast.cpp +++ b/indra/newview/lltoast.cpp @@ -49,10 +49,8 @@ LLToast::LLToast(LLToast::Params p) : LLFloater(LLSD()), mCanBeStored(p.can_be_stored), mHideBtnEnabled(p.enable_hide_btn), mIsModal(p.is_modal), - mIsTipNotification(p.is_tip), mHideBtn(NULL), mNotification(p.notification), - mIsViewed(false), mHideBtnPressed(false) { LLUICtrlFactory::getInstance()->buildFloater(this, "panel_toast.xml", NULL); @@ -147,7 +145,6 @@ bool LLToast::timerHasExpired() void LLToast::hide() { setVisible(FALSE); - mIsViewed = false; mTimer.stop(); mOnFade(this); } @@ -228,9 +225,8 @@ void LLToast::onMouseEnter(S32 x, S32 y, MASK mask) { mOnToastHover(this, MOUSE_ENTER); - setVisibleAndFrontmost(); setBackgroundOpaque(TRUE); - if(mCanFade && !mIsViewed) + if(mCanFade) { mTimer.stop(); } @@ -246,7 +242,7 @@ void LLToast::onMouseLeave(S32 x, S32 y, MASK mask) { mOnToastHover(this, MOUSE_LEAVE); - if(mCanFade && !mIsViewed) + if(mCanFade) { mTimer.start(); } diff --git a/indra/newview/lltoast.h b/indra/newview/lltoast.h index a4dee1e386..969f3fd1cb 100644 --- a/indra/newview/lltoast.h +++ b/indra/newview/lltoast.h @@ -107,8 +107,6 @@ public: // void stopTimer() { mTimer.stop(); } // - void close() { die(); } - // virtual void draw(); // virtual void setVisible(BOOL show); @@ -121,8 +119,6 @@ public: // get information whether the notification corresponding to the toast is responded or not bool getIsNotificationUnResponded(); // - bool isViewed() { return mIsViewed; } - // void setCanFade(bool can_fade); // void setCanBeStored(bool can_be_stored) { mCanBeStored = can_be_stored; } @@ -163,8 +159,6 @@ private: LLButton* mHideBtn; LLColor4 mBgColor; - bool mIsViewed; - bool mIsTipNotification; bool mCanFade; bool mIsModal; bool mCanBeStored; diff --git a/indra/newview/lltoastalertpanel.cpp b/indra/newview/lltoastalertpanel.cpp index 5d67015526..4309f56710 100644 --- a/indra/newview/lltoastalertpanel.cpp +++ b/indra/newview/lltoastalertpanel.cpp @@ -66,8 +66,7 @@ static const S32 HPAD = 25; static const S32 BTN_HPAD = 8; LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal) - : LLFloater(LLSD()), - LLToastPanel(notification), + : LLToastPanel(notification), mDefaultOption( 0 ), mCheck(NULL), mCaution(notification->getPriority() >= NOTIFICATION_PRIORITY_HIGH), @@ -304,8 +303,8 @@ LLToastAlertPanel::LLToastAlertPanel( LLNotificationPtr notification, bool modal setCheckBox(LLNotifications::instance().getGlobalString("alwayschoose"), ignore_label); } - gFloaterView->adjustToFitScreen(this, FALSE); - LLFloater::setFocus(TRUE); + // *TODO: check necessity of this code + //gFloaterView->adjustToFitScreen(this, FALSE); if (mLineEditor) { mLineEditor->setFocus(TRUE); @@ -352,12 +351,14 @@ bool LLToastAlertPanel::setCheckBox( const std::string& check_title, const std:: void LLToastAlertPanel::setVisible( BOOL visible ) { - LLToastPanel::setVisible( visible ); - - if( visible ) + // only make the "ding" sound if it's newly visible + if( visible && !LLToastPanel::getVisible() ) { make_ui_sound("UISndAlert"); } + + LLToastPanel::setVisible( visible ); + } LLToastAlertPanel::~LLToastAlertPanel() @@ -366,9 +367,13 @@ LLToastAlertPanel::~LLToastAlertPanel() BOOL LLToastAlertPanel::hasTitleBar() const { + // *TODO: check necessity of this code + /* return (getCurrentTitle() != "" && getCurrentTitle() != " ") // has title || isMinimizeable() || isCloseable(); + */ + return false; } BOOL LLToastAlertPanel::handleKeyHere(KEY key, MASK mask ) @@ -453,7 +458,6 @@ void LLToastAlertPanel::onButtonPressed( const LLSD& data, S32 button ) } mNotification->respond(response); // new notification reponse - closeFloater(); // deletes self } void LLToastAlertPanel::onClickIgnore(LLUICtrl* ctrl) diff --git a/indra/newview/lltoastalertpanel.h b/indra/newview/lltoastalertpanel.h index 543c14d404..af0c9a9ddd 100644 --- a/indra/newview/lltoastalertpanel.h +++ b/indra/newview/lltoastalertpanel.h @@ -52,8 +52,7 @@ class LLLineEditor; */ class LLToastAlertPanel - : public LLToastPanel, - public LLFloater + : public LLToastPanel { public: typedef bool (*display_callback_t)(S32 modal); diff --git a/indra/newview/lltoastgroupnotifypanel.cpp b/indra/newview/lltoastgroupnotifypanel.cpp index 8a61f6cfda..6f26b4077c 100644 --- a/indra/newview/lltoastgroupnotifypanel.cpp +++ b/indra/newview/lltoastgroupnotifypanel.cpp @@ -40,12 +40,12 @@ #include "lliconctrl.h" #include "llnotify.h" #include "lltextbox.h" -#include "llviewertexteditor.h" +#include "lltexteditor.h" #include "lluiconstants.h" #include "llui.h" #include "llviewercontrol.h" #include "lltrans.h" -#include "llinitparam.h" +#include "llstyle.h" #include "llglheaders.h" #include "llagent.h" @@ -75,23 +75,25 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification const std::string& from_name = payload["sender_name"].asString(); std::stringstream from; from << from_name << "/" << groupData.mName; - LLTextBox* pTitleText = this->getChild<LLTextBox>("title"); + LLTextBox* pTitleText = getChild<LLTextBox>("title"); pTitleText->setValue(from.str()); //message body const std::string& subject = payload["subject"].asString(); const std::string& message = payload["message"].asString(); - LLTextEditor* pMessageText = getChild< LLTextEditor>("message"); + LLTextEditor* pMessageText = getChild<LLTextEditor>("message"); pMessageText->setValue(""); pMessageText->setEnabled(FALSE); - pMessageText->setTakesFocus(FALSE); - static const LLStyleSP headerstyle(new LLStyle(true, textColor, - "SansSerifBig")); - static const LLStyleSP datestyle(new LLStyle(true, textColor, "serif")); + LLStyle::Params date_style; + date_style.color = textColor; + date_style.font.name = "SANSSERIF"; - pMessageText->appendStyledText(subject + "\n",false,false,headerstyle); + LLStyle::Params header_style_params; + header_style_params.color = textColor; + header_style_params.font = LLFontGL::getFontSansSerifBig(); + pMessageText->appendStyledText(subject + "\n",false,false,header_style_params); std::string timeStr = "["+LLTrans::getString("UTCTimeWeek")+"],[" +LLTrans::getString("UTCTimeDay")+"] [" @@ -106,7 +108,10 @@ LLToastGroupNotifyPanel::LLToastGroupNotifyPanel(LLNotificationPtr& notification LLSD substitution; substitution["datetime"] = (S32) notice_date.secondsSinceEpoch(); LLStringUtil::format(timeStr, substitution); - pMessageText->appendStyledText(timeStr, false, false, datestyle); + LLStyle::Params date_style_params; + date_style_params.color = textColor; + date_style_params.font = LLFontGL::getFontMonospace(); + pMessageText->appendStyledText(timeStr, false, false, date_style); pMessageText->appendColoredText(std::string("\n\n") + message, false, false, textColor); diff --git a/indra/newview/lltoastimpanel.cpp b/indra/newview/lltoastimpanel.cpp new file mode 100644 index 0000000000..e41181c9e1 --- /dev/null +++ b/indra/newview/lltoastimpanel.cpp @@ -0,0 +1,94 @@ +/** + * @file lltoastimpanel.cpp + * @brief Panel for IM toasts. + * + * $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$ + */ + +#include "llviewerprecompiledheaders.h" +#include "lltoastimpanel.h" +#include "llimpanel.h" + +const S32 LLToastIMPanel::MAX_MESSAGE_HEIGHT = 50; +const S32 LLToastIMPanel::CAPTION_HEIGHT = 30; +const S32 LLToastIMPanel::TOP_PAD = 5; + +//-------------------------------------------------------------------------- +LLToastIMPanel::LLToastIMPanel(LLToastIMPanel::Params &p) : LLToastPanel(p.notification), + mAvatar(NULL), mUserName(NULL), + mTime(NULL), mMessage(NULL), + mReplyBtn(NULL) +{ + LLUICtrlFactory::getInstance()->buildPanel(this, "panel_instant_message.xml"); + + mAvatar = getChild<LLAvatarIconCtrl>("avatar"); + mUserName = getChild<LLTextBox>("user_name"); + mTime = getChild<LLTextBox>("time_box"); + mMessage = getChild<LLTextBox>("message"); + mReplyBtn = getChild<LLButton>("reply"); + + mMessage->setValue(p.message); + mAvatar->setValue(p.avatar_id); + mUserName->setValue(p.from); + mTime->setValue(p.time); + mSessionID = p.session_id; + + mReplyBtn->setClickedCallback(boost::bind(&LLToastIMPanel::onClickReplyBtn, this)); + + snapToMessageHeight(); +} + +//-------------------------------------------------------------------------- +LLToastIMPanel::~LLToastIMPanel() +{ +} + +//-------------------------------------------------------------------------- +void LLToastIMPanel::snapToMessageHeight() +{ + S32 required_text_height = mMessage->getTextPixelHeight(); + S32 text_height = llmin(required_text_height, MAX_MESSAGE_HEIGHT); + LLRect text_rect = mMessage->getRect(); + LLRect btn_rect = mReplyBtn->getRect(); + + + mMessage->reshape( text_rect.getWidth(), text_height, TRUE); + mMessage->setValue(mMessage->getText()); + + S32 panel_height = CAPTION_HEIGHT + text_height + btn_rect.getHeight() + TOP_PAD*5; + reshape( getRect().getWidth(), panel_height, TRUE); +} + +//-------------------------------------------------------------------------- +void LLToastIMPanel::onClickReplyBtn() +{ + LLIMFloater::toggle(mSessionID); +} + +//-------------------------------------------------------------------------- + diff --git a/indra/newview/lltoastimpanel.h b/indra/newview/lltoastimpanel.h new file mode 100644 index 0000000000..7f5d6eca1d --- /dev/null +++ b/indra/newview/lltoastimpanel.h @@ -0,0 +1,79 @@ +/** + * @file lltoastimpanel.h + * @brief Panel for IM toasts. + * + * $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 LLTOASTIMPANEL_H_ +#define LLTOASTIMPANEL_H_ + + +#include "lltoastpanel.h" +#include "lltextbox.h" +#include "llbutton.h" +#include "llavatariconctrl.h" + + +class LLToastIMPanel: public LLToastPanel +{ +public: + struct Params : public LLInitParam::Block<Params> + { + LLNotificationPtr notification; + LLUUID avatar_id; + LLUUID session_id; + std::string from; + std::string time; + std::string message; + + Params() {} + }; + + LLToastIMPanel(LLToastIMPanel::Params &p); + virtual ~LLToastIMPanel(); + +private: + static const S32 MAX_MESSAGE_HEIGHT; + static const S32 CAPTION_HEIGHT; + static const S32 TOP_PAD; + + void onClickReplyBtn(); + void snapToMessageHeight(); + + LLUUID mSessionID; + LLAvatarIconCtrl* mAvatar; + LLTextBox* mUserName; + LLTextBox* mTime; + LLTextBox* mMessage; + LLButton* mReplyBtn; +}; + +#endif // LLTOASTIMPANEL_H_ + + diff --git a/indra/newview/lltoastnotifypanel.cpp b/indra/newview/lltoastnotifypanel.cpp index a7b57802c1..844c54da6a 100644 --- a/indra/newview/lltoastnotifypanel.cpp +++ b/indra/newview/lltoastnotifypanel.cpp @@ -107,7 +107,6 @@ LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToas common_params.rect(LLRect(x, y, x+32, TOP-32)); common_params.mouse_opaque(false); common_params.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP); - common_params.tab_stop(false); if (mIsTip) { @@ -180,8 +179,6 @@ LLToastNotifyPanel::LLToastNotifyPanel(LLNotificationPtr& notification) : LLToas params.mouse_opaque(false); params.bg_readonly_color(LLColor4::transparent); params.text_readonly_color(LLUIColorTable::instance().getColor("NotifyTextColor")); - params.takes_non_scroll_clicks(false); - params.hide_scrollbar(true); params.enabled(false); params.hide_border(true); text = LLUICtrlFactory::create<LLTextEditor> (params); diff --git a/indra/newview/lltoastpanel.cpp b/indra/newview/lltoastpanel.cpp index c39bac97a8..28052a33be 100644 --- a/indra/newview/lltoastpanel.cpp +++ b/indra/newview/lltoastpanel.cpp @@ -45,16 +45,8 @@ LLToastPanel::~LLToastPanel() std::string LLToastPanel::getTitle() { -// *TODO: localize header of Title -/* std::string title; - std::string notification_type = mNotification->getType(); - - if( notification_type == "groupnotify" ) - { - title = LLTrans::getString("TitleGroup"); - } -*/ - return (mNotification->getName() + "\n" + mNotification->getMessage()); + // *TODO: create Title and localize it. If it will be required. + return mNotification->getMessage(); } diff --git a/indra/newview/lltoolcomp.cpp b/indra/newview/lltoolcomp.cpp index cb964082b8..fff18df442 100644 --- a/indra/newview/lltoolcomp.cpp +++ b/indra/newview/lltoolcomp.cpp @@ -765,10 +765,6 @@ void LLToolCompGun::onMouseCaptureLost() return; } mCur->onMouseCaptureLost(); - - // JC - I don't know if this is necessary. Maybe we could lose capture - // if someone ALT-Tab's out when in mouselook. - setCurrentTool( (LLTool*) mGun ); } void LLToolCompGun::handleSelect() diff --git a/indra/newview/lltoolcomp.h b/indra/newview/lltoolcomp.h index b24ba25d5a..81ed0ba8e4 100644 --- a/indra/newview/lltoolcomp.h +++ b/indra/newview/lltoolcomp.h @@ -229,6 +229,7 @@ public: virtual void onMouseCaptureLost(); virtual void handleSelect(); virtual void handleDeselect(); + virtual LLTool* getOverrideTool(MASK mask) { return NULL; } protected: LLToolGun* mGun; diff --git a/indra/newview/lltooldraganddrop.cpp b/indra/newview/lltooldraganddrop.cpp index 70fa2f715b..b5dd34df15 100644 --- a/indra/newview/lltooldraganddrop.cpp +++ b/indra/newview/lltooldraganddrop.cpp @@ -72,7 +72,7 @@ #include "object_flags.h" #include "llimview.h" #include "llrootview.h" - +#include "llagentui.h" // MAX ITEMS is based on (sizeof(uuid)+2) * count must be < MTUBYTES // or 18 * count < 1200 => count < 1200/18 => 66. I've cut it down a @@ -1563,7 +1563,7 @@ void LLToolDragAndDrop::commitGiveInventoryItem(const LLUUID& to_agent, { if(!item) return; std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); LLUUID transaction_id; transaction_id.generate(); const S32 BUCKET_SIZE = sizeof(U8) + UUID_BYTES; @@ -1762,7 +1762,7 @@ void LLToolDragAndDrop::commitGiveInventoryCategory(const LLUUID& to_agent, else { std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); LLUUID transaction_id; transaction_id.generate(); S32 bucket_size = (sizeof(U8) + UUID_BYTES) * (count + 1); diff --git a/indra/newview/lltoolface.cpp b/indra/newview/lltoolface.cpp index b335d7125a..1d78dc5019 100644 --- a/indra/newview/lltoolface.cpp +++ b/indra/newview/lltoolface.cpp @@ -40,7 +40,6 @@ #include "v3math.h" // Viewer includes -#include "llagent.h" #include "llviewercontrol.h" #include "llselectmgr.h" #include "llviewerobject.h" diff --git a/indra/newview/lltoolgun.cpp b/indra/newview/lltoolgun.cpp index b70cff3869..8accf6babf 100644 --- a/indra/newview/lltoolgun.cpp +++ b/indra/newview/lltoolgun.cpp @@ -52,7 +52,8 @@ #include "llwindow.h" // setMouseClipping() LLToolGun::LLToolGun( LLToolComposite* composite ) -: LLTool( std::string("gun"), composite ) +: LLTool( std::string("gun"), composite ), + mIsSelected(FALSE) { } @@ -61,6 +62,7 @@ void LLToolGun::handleSelect() gViewerWindow->hideCursor(); gViewerWindow->moveCursorToCenter(); gViewerWindow->mWindow->setMouseClipping(TRUE); + mIsSelected = TRUE; } void LLToolGun::handleDeselect() @@ -68,6 +70,7 @@ void LLToolGun::handleDeselect() gViewerWindow->moveCursorToCenter(); gViewerWindow->showCursor(); gViewerWindow->mWindow->setMouseClipping(FALSE); + mIsSelected = FALSE; } BOOL LLToolGun::handleMouseDown(S32 x, S32 y, MASK mask) @@ -80,7 +83,7 @@ BOOL LLToolGun::handleMouseDown(S32 x, S32 y, MASK mask) BOOL LLToolGun::handleHover(S32 x, S32 y, MASK mask) { - if( gAgent.cameraMouselook() ) + if( gAgent.cameraMouselook() && mIsSelected ) { const F32 NOMINAL_MOUSE_SENSITIVITY = 0.0025f; diff --git a/indra/newview/lltoolgun.h b/indra/newview/lltoolgun.h index 4e945d796c..4644e686b7 100644 --- a/indra/newview/lltoolgun.h +++ b/indra/newview/lltoolgun.h @@ -52,6 +52,8 @@ public: virtual LLTool* getOverrideTool(MASK mask) { return NULL; } virtual BOOL clipMouseWhenDown() { return FALSE; } +private: + BOOL mIsSelected; }; #endif diff --git a/indra/newview/lltoolmgr.cpp b/indra/newview/lltoolmgr.cpp index cf3d15a12a..d52e0b4b80 100644 --- a/indra/newview/lltoolmgr.cpp +++ b/indra/newview/lltoolmgr.cpp @@ -357,22 +357,20 @@ void LLToolMgr::clearTransientTool() } -// The "gun tool", used for handling mouselook, captures the mouse and -// locks it within the window. When the app loses focus we need to -// release this locking. void LLToolMgr::onAppFocusLost() { - mSavedTool = mBaseTool; - mBaseTool = gToolNull; + if (mSelectedTool) + { + mSelectedTool->handleDeselect(); + } updateToolStatus(); } void LLToolMgr::onAppFocusGained() { - if (mSavedTool) + if (mSelectedTool) { - mBaseTool = mSavedTool; - mSavedTool = NULL; + mSelectedTool->handleSelect(); } updateToolStatus(); } diff --git a/indra/newview/lltoolmorph.cpp b/indra/newview/lltoolmorph.cpp index ae3f2f55de..d7d7b5f44b 100644 --- a/indra/newview/lltoolmorph.cpp +++ b/indra/newview/lltoolmorph.cpp @@ -37,7 +37,7 @@ #include "llrender.h" // Library includes -#include "audioengine.h" +#include "llaudioengine.h" #include "llviewercontrol.h" #include "llfontgl.h" #include "sound_ids.h" diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index fab336f17d..235d4acf9d 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -36,11 +36,11 @@ #include "indra_constants.h" #include "llclickaction.h" -#include "llmediabase.h" // for status codes #include "llparcel.h" #include "llagent.h" #include "llviewercontrol.h" +#include "llfocusmgr.h" #include "llfirstuse.h" #include "llfloaterland.h" #include "llfloaterreg.h" @@ -48,7 +48,6 @@ #include "llhoverview.h" #include "llhudeffecttrail.h" #include "llhudmanager.h" -#include "llmediamanager.h" #include "llmenugl.h" #include "llmutelist.h" #include "llselectmgr.h" @@ -65,6 +64,7 @@ #include "llviewerwindow.h" #include "llviewermedia.h" #include "llvoavatarself.h" +#include "llviewermediafocus.h" #include "llworld.h" #include "llui.h" #include "llweb.h" @@ -73,13 +73,15 @@ extern void handle_buy(void*); extern BOOL gDebugClicks; +static bool handle_media_click(const LLPickInfo& info); +static bool handle_media_hover(const LLPickInfo& info); static void handle_click_action_play(); static void handle_click_action_open_media(LLPointer<LLViewerObject> objectp); static ECursorType cursor_from_parcel_media(U8 click_action); LLToolPie::LLToolPie() -: LLTool(std::string("Select")), +: LLTool(std::string("Pie")), mGrabMouseButtonDown( FALSE ), mMouseOutsideSlop( FALSE ), mClickAction(0) @@ -117,6 +119,11 @@ BOOL LLToolPie::handleRightMouseUp(S32 x, S32 y, MASK mask) return LLTool::handleRightMouseUp(x, y, mask); } +BOOL LLToolPie::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + return LLViewerMediaFocus::getInstance()->handleScrollWheel(x, y, clicks); +} + // static void LLToolPie::rightMouseCallback(const LLPickInfo& pick_info) { @@ -150,6 +157,7 @@ BOOL LLToolPie::pickLeftMouseDownCallback() } } + gFocusMgr.setKeyboardFocus(NULL); return LLTool::handleMouseDown(x, y, mask); } @@ -167,9 +175,11 @@ BOOL LLToolPie::pickLeftMouseDownCallback() parent = object->getRootEdit(); } + BOOL touchable = (object && object->flagHandleTouch()) || (parent && parent->flagHandleTouch()); + // If it's a left-click, and we have a special action, do it. if (useClickAction(mask, object, parent)) { @@ -192,6 +202,8 @@ BOOL LLToolPie::pickLeftMouseDownCallback() if ((gAgent.getAvatarObject() != NULL) && (!gAgent.getAvatarObject()->isSitting())) // agent not already sitting { handle_sit_or_stand(); + // put focus in world when sitting on an object + gFocusMgr.setKeyboardFocus(NULL); return TRUE; } // else nothing (fall through to touch) @@ -244,6 +256,14 @@ BOOL LLToolPie::pickLeftMouseDownCallback() } } + if (handle_media_click(mPick)) + { + return FALSE; + } + + // put focus back "in world" + gFocusMgr.setKeyboardFocus(NULL); + // Switch to grab tool if physical or triggerable if (object && !object->isAvatar() && @@ -461,13 +481,13 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) mMouseOutsideSlop = TRUE; } */ - + + // FIXME: This was in the pluginapi branch, but I don't think it's correct. +// gViewerWindow->getWindow()->setCursor(UI_CURSOR_ARROW); + LLViewerObject *object = NULL; LLViewerObject *parent = NULL; - if (gHoverView) - { - object = gViewerWindow->getHoverPick().getObject(); - } + object = gViewerWindow->getHoverPick().getObject(); if (object) { @@ -480,6 +500,11 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) gViewerWindow->setCursor(cursor); lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; } + else if (handle_media_hover(gViewerWindow->getHoverPick())) + { + // cursor set by media object + lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; + } else if ((object && !object->isAvatar() && object->usePhysics()) || (parent && !parent->isAvatar() && parent->usePhysics())) { @@ -496,6 +521,15 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) { gViewerWindow->setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by LLToolPie (inactive)" << llendl; + + if(!object) + { + // We need to clear media hover flag + if (LLViewerMediaFocus::getInstance()->getMouseOverFlag()) + { + LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + } + } } return TRUE; @@ -644,14 +678,14 @@ static void handle_click_action_play() LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); if (!parcel) return; - LLMediaBase::EStatus status = LLViewerParcelMedia::getStatus(); + LLViewerMediaImpl::EMediaStatus status = LLViewerParcelMedia::getStatus(); switch(status) { - case LLMediaBase::STATUS_STARTED: + case LLViewerMediaImpl::MEDIA_PLAYING: LLViewerParcelMedia::pause(); break; - case LLMediaBase::STATUS_PAUSED: + case LLViewerMediaImpl::MEDIA_PAUSED: LLViewerParcelMedia::start(); break; @@ -661,6 +695,111 @@ static void handle_click_action_play() } } +static bool handle_media_click(const LLPickInfo& pick) +{ + //FIXME: how do we handle object in different parcel than us? + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + LLPointer<LLViewerObject> objectp = pick.getObject(); + + + if (!parcel || + objectp.isNull() || + pick.mObjectFace < 0 || + pick.mObjectFace >= objectp->getNumTEs()) + { + LLSelectMgr::getInstance()->deselect(); + LLViewerMediaFocus::getInstance()->clearFocus(); + + return false; + } + + + + // HACK: This is directly referencing an impl name. BAD! + // This can be removed when we have a truly generic media browser that only + // builds an impl based on the type of url it is passed. + + // is media playing on this face? + const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); + + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); + if (tep + && media_impl.notNull() + && media_impl->hasMedia() + && gSavedSettings.getBOOL("MediaOnAPrimUI")) + { + LLObjectSelectionHandle selection = LLViewerMediaFocus::getInstance()->getSelection(); + if (! selection->contains(pick.getObject(), pick.mObjectFace)) + { + LLViewerMediaFocus::getInstance()->setFocusFace(TRUE, pick.getObject(), pick.mObjectFace, media_impl); + } + else + { + media_impl->mouseDown(pick.mXYCoords.mX, pick.mXYCoords.mY); + media_impl->mouseCapture(); // the mouse-up will happen when capture is lost + } + + return true; + } + + LLSelectMgr::getInstance()->deselect(); + LLViewerMediaFocus::getInstance()->clearFocus(); + + return false; +} + +static bool handle_media_hover(const LLPickInfo& pick) +{ + //FIXME: how do we handle object in different parcel than us? + LLParcel* parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (!parcel) return false; + + LLPointer<LLViewerObject> objectp = pick.getObject(); + + // Early out cases. Must clear mouse over media focus flag + // did not hit an object or did not hit a valid face + if ( objectp.isNull() || + pick.mObjectFace < 0 || + pick.mObjectFace >= objectp->getNumTEs() ) + { + LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + return false; + } + + + // HACK: This is directly referencing an impl name. BAD! + // This can be removed when we have a truly generic media browser that only + // builds an impl based on the type of url it is passed. + + // is media playing on this face? + const LLTextureEntry* tep = objectp->getTE(pick.mObjectFace); + viewer_media_t media_impl = LLViewerMedia::getMediaImplFromTextureID(tep->getID()); + if (tep + && media_impl.notNull() + && media_impl->hasMedia() + && gSavedSettings.getBOOL("MediaOnAPrimUI")) + { + if(LLViewerMediaFocus::getInstance()->getFocus()) + { + media_impl->mouseMove(pick.mXYCoords.mX, pick.mXYCoords.mY); + } + + // Set mouse over flag if unset + if (! LLViewerMediaFocus::getInstance()->getMouseOverFlag()) + { + LLSelectMgr::getInstance()->setHoverObject(objectp, pick.mObjectFace); + LLViewerMediaFocus::getInstance()->setMouseOverFlag(true, media_impl); + LLViewerMediaFocus::getInstance()->setPickInfo(pick); + } + + return true; + } + LLViewerMediaFocus::getInstance()->setMouseOverFlag(false); + + return false; +} + + static void handle_click_action_open_media(LLPointer<LLViewerObject> objectp) { //FIXME: how do we handle object in different parcel than us? @@ -675,7 +814,7 @@ static void handle_click_action_open_media(LLPointer<LLViewerObject> objectp) if( face < 0 || face >= objectp->getNumTEs() ) return; // is media playing on this face? - if (!LLViewerMedia::isActiveMediaTexture(objectp->getTE(face)->getID())) + if (LLViewerMedia::getMediaImplFromTextureID(objectp->getTE(face)->getID()) != NULL) { handle_click_action_play(); return; @@ -685,18 +824,7 @@ static void handle_click_action_open_media(LLPointer<LLViewerObject> objectp) std::string media_type = std::string ( parcel->getMediaType() ); LLStringUtil::trim(media_url); - // Get the scheme, see if that is handled as well. - LLURI uri(media_url); - std::string media_scheme = uri.scheme() != "" ? uri.scheme() : "http"; - - // HACK: This is directly referencing an impl name. BAD! - // This can be removed when we have a truly generic media browser that only - // builds an impl based on the type of url it is passed. - - if( LLMediaManager::getInstance()->supportsMediaType( "LLMediaImplLLMozLib", media_scheme, media_type ) ) - { - LLWeb::loadURL(media_url); - } + LLWeb::loadURL(media_url); } static ECursorType cursor_from_parcel_media(U8 click_action) @@ -714,19 +842,12 @@ static ECursorType cursor_from_parcel_media(U8 click_action) std::string media_type = std::string ( parcel->getMediaType() ); LLStringUtil::trim(media_url); - // Get the scheme, see if that is handled as well. - LLURI uri(media_url); - std::string media_scheme = uri.scheme() != "" ? uri.scheme() : "http"; - - if( LLMediaManager::getInstance()->supportsMediaType( "LLMediaImplLLMozLib", media_scheme, media_type ) ) - { - open_cursor = UI_CURSOR_TOOLMEDIAOPEN; - } + open_cursor = UI_CURSOR_TOOLMEDIAOPEN; - LLMediaBase::EStatus status = LLViewerParcelMedia::getStatus(); + LLViewerMediaImpl::EMediaStatus status = LLViewerParcelMedia::getStatus(); switch(status) { - case LLMediaBase::STATUS_STARTED: + case LLViewerMediaImpl::MEDIA_PLAYING: return click_action == CLICK_ACTION_PLAY ? UI_CURSOR_TOOLPAUSE : open_cursor; default: return UI_CURSOR_TOOLPLAY; diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index d2437e813d..a55e435282 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -51,6 +51,7 @@ public: virtual BOOL handleRightMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask); + virtual BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); virtual void render(); diff --git a/indra/newview/lltoolplacer.cpp b/indra/newview/lltoolplacer.cpp index 607818305e..de68dd6153 100644 --- a/indra/newview/lltoolplacer.cpp +++ b/indra/newview/lltoolplacer.cpp @@ -56,7 +56,7 @@ #include "llvolumemessage.h" #include "llhudmanager.h" #include "llagent.h" -#include "audioengine.h" +#include "llaudioengine.h" #include "llhudeffecttrail.h" #include "llviewerobjectlist.h" #include "llviewercamera.h" diff --git a/indra/newview/lltoolselectland.cpp b/indra/newview/lltoolselectland.cpp index 07c996a84f..5c8b08db3b 100644 --- a/indra/newview/lltoolselectland.cpp +++ b/indra/newview/lltoolselectland.cpp @@ -38,7 +38,6 @@ #include "llparcel.h" // Viewer includes -#include "llagent.h" #include "llviewercontrol.h" #include "llfloatertools.h" #include "llselectmgr.h" diff --git a/indra/newview/llurldispatcher.cpp b/indra/newview/llurldispatcher.cpp index 9fb6d1c874..cb68045310 100644 --- a/indra/newview/llurldispatcher.cpp +++ b/indra/newview/llurldispatcher.cpp @@ -37,8 +37,7 @@ #include "llagent.h" // teleportViaLocation() #include "llcommandhandler.h" #include "llfloaterdirectory.h" -#include "llfloaterhtml.h" -#include "llfloaterhtmlhelp.h" +#include "llfloatermediabrowser.h" #include "llfloaterreg.h" #include "llfloaterurldisplay.h" #include "llfloaterworldmap.h" @@ -57,7 +56,7 @@ class LLURLDispatcherImpl { public: static bool dispatch(const std::string& url, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser); // returns true if handled or explicitly blocked. @@ -66,7 +65,7 @@ public: private: static bool dispatchCore(const std::string& url, bool right_mouse, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser); // handles both left and right click @@ -76,7 +75,7 @@ private: static bool dispatchApp(const std::string& url, bool right_mouse, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser); // Handles secondlife:///app/agent/<agent_id>/about and similar // by showing panel in Search floater. @@ -102,7 +101,7 @@ private: // static bool LLURLDispatcherImpl::dispatchCore(const std::string& url, bool right_mouse, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser) { if (url.empty()) return false; @@ -122,7 +121,7 @@ bool LLURLDispatcherImpl::dispatchCore(const std::string& url, // static bool LLURLDispatcherImpl::dispatch(const std::string& url, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser) { llinfos << "url: " << url << llendl; @@ -135,7 +134,7 @@ bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url) { llinfos << "url: " << url << llendl; const bool right_click = true; - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; const bool trusted_browser = false; return dispatchCore(url, right_click, web, trusted_browser); } @@ -156,7 +155,7 @@ bool LLURLDispatcherImpl::dispatchHelp(const std::string& url, bool right_mouse) // static bool LLURLDispatcherImpl::dispatchApp(const std::string& url, bool right_mouse, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser) { if (!LLSLURL::isSLURL(url)) @@ -327,7 +326,7 @@ public: LLTeleportHandler() : LLCommandHandler("teleport", true) { } bool handle(const LLSD& tokens, const LLSD& query_map, - LLWebBrowserCtrl* web) + LLMediaCtrl* web) { // construct a "normal" SLURL, resolve the region to // a global position, and teleport to it @@ -355,7 +354,7 @@ LLTeleportHandler gTeleportHandler; // static bool LLURLDispatcher::dispatch(const std::string& url, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser) { return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); @@ -377,6 +376,6 @@ bool LLURLDispatcher::dispatchFromTextEditor(const std::string& url) // click on it. // *TODO: Make this trust model more refined. JC const bool trusted_browser = true; - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; return LLURLDispatcherImpl::dispatch(url, web, trusted_browser); } diff --git a/indra/newview/llurldispatcher.h b/indra/newview/llurldispatcher.h index 15e3ec73d8..ff8a351253 100644 --- a/indra/newview/llurldispatcher.h +++ b/indra/newview/llurldispatcher.h @@ -32,14 +32,14 @@ #ifndef LLURLDISPATCHER_H #define LLURLDISPATCHER_H -class LLWebBrowserCtrl; +class LLMediaCtrl; class LLURLDispatcher { public: static bool dispatch(const std::string& url, - LLWebBrowserCtrl* web, + LLMediaCtrl* web, bool trusted_browser); // At startup time and on clicks in internal web browsers, // teleport, open map, or run requested command. @@ -48,7 +48,7 @@ public: // secondlife:///app/agent/3d6181b0-6a4b-97ef-18d8-722652995cf1/show // sl://app/foo/bar // @param web - // Pointer to LLWebBrowserCtrl sending URL, can be NULL + // Pointer to LLMediaCtrl sending URL, can be NULL // @param trusted_browser // True if coming inside the app AND from a brower instance // that navigates to trusted (Linden Lab) pages. diff --git a/indra/newview/llvieweraudio.cpp b/indra/newview/llvieweraudio.cpp index a1e55c1137..49506db173 100644 --- a/indra/newview/llvieweraudio.cpp +++ b/indra/newview/llvieweraudio.cpp @@ -32,7 +32,7 @@ #include "llviewerprecompiledheaders.h" -#include "audioengine.h" +#include "llaudioengine.h" #include "llagent.h" #include "llappviewer.h" #include "llvieweraudio.h" @@ -128,7 +128,6 @@ void audio_update_volume(bool force_update) gAudiop->setMasterGain ( master_volume ); gAudiop->setDopplerFactor(gSavedSettings.getF32("AudioLevelDoppler")); - gAudiop->setDistanceFactor(gSavedSettings.getF32("AudioLevelDistance")); gAudiop->setRolloffFactor(gSavedSettings.getF32("AudioLevelRolloff")); #ifdef kAUDIO_ENABLE_WIND gAudiop->enableWind(!mute_audio); diff --git a/indra/newview/llviewercontrol.cpp b/indra/newview/llviewercontrol.cpp index 4640745e8c..2c1707e49f 100644 --- a/indra/newview/llviewercontrol.cpp +++ b/indra/newview/llviewercontrol.cpp @@ -39,7 +39,7 @@ #include "llwindow.h" // getGamma() // For Listeners -#include "audioengine.h" +#include "llaudioengine.h" #include "llagent.h" #include "llconsole.h" #include "lldrawpoolterrain.h" @@ -523,7 +523,6 @@ void settings_setup_listeners() gSavedSettings.getControl("AudioLevelMusic")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); gSavedSettings.getControl("AudioLevelMedia")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); gSavedSettings.getControl("AudioLevelVoice")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); - gSavedSettings.getControl("AudioLevelDistance")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); gSavedSettings.getControl("AudioLevelDoppler")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); gSavedSettings.getControl("AudioLevelRolloff")->getSignal()->connect(boost::bind(&handleAudioVolumeChanged, _2)); gSavedSettings.getControl("AudioStreamingMusic")->getSignal()->connect(boost::bind(&handleAudioStreamMusicChanged, _2)); diff --git a/indra/newview/llviewerdisplay.cpp b/indra/newview/llviewerdisplay.cpp index f08c287529..8e65c7e65e 100644 --- a/indra/newview/llviewerdisplay.cpp +++ b/indra/newview/llviewerdisplay.cpp @@ -207,11 +207,17 @@ void display_stats() } } +static LLFastTimer::DeclareTimer FTM_PICK("Picking"); +static LLFastTimer::DeclareTimer FTM_RENDER("Render", true); +static LLFastTimer::DeclareTimer FTM_UPDATE_SKY("Update Sky"); +static LLFastTimer::DeclareTimer FTM_UPDATE_TEXTURES("Update Textures"); +static LLFastTimer::DeclareTimer FTM_IMAGE_UPDATE("Update Images"); + // Paint the display! void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) { LLMemType mt_render(LLMemType::MTYPE_RENDER); - LLFastTimer t(LLFastTimer::FTM_RENDER); + LLFastTimer t(FTM_RENDER); if (LLPipeline::sRenderFrameTest) { @@ -258,7 +264,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) gViewerWindow->checkSettings(); { - LLFastTimer ftm(LLFastTimer::FTM_PICK); + LLFastTimer ftm(FTM_PICK); LLAppViewer::instance()->pingMainloopTimeout("Display:Pick"); gViewerWindow->performPick(); } @@ -504,7 +510,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_DYNAMIC_TEXTURES)) { LLAppViewer::instance()->pingMainloopTimeout("Display:DynamicTextures"); - LLFastTimer t(LLFastTimer::FTM_UPDATE_TEXTURES); + LLFastTimer t(FTM_UPDATE_TEXTURES); if (LLViewerDynamicTexture::updateAllInstances()) { gGL.setColorMask(true, true); @@ -616,7 +622,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) { LLMemType mt_ds(LLMemType::MTYPE_DISPLAY_SWAP); { - LLFastTimer ftm(LLFastTimer::FTM_CLIENT_COPY); + LLFastTimer ftm(FTM_CLIENT_COPY); LLVertexBuffer::clientCopy(0.016); } @@ -687,7 +693,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) { LLMemType mt_iu(LLMemType::MTYPE_DISPLAY_IMAGE_UPDATE); - LLFastTimer t(LLFastTimer::FTM_IMAGE_UPDATE); + LLFastTimer t(FTM_IMAGE_UPDATE); LLViewerTexture::updateClass(LLViewerCamera::getInstance()->getVelocityStat()->getMean(), LLViewerCamera::getInstance()->getAngularVelocityStat()->getMean()); @@ -730,7 +736,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) { LLMemType mt_ds(LLMemType::MTYPE_DISPLAY_SKY); LLAppViewer::instance()->pingMainloopTimeout("Display:Sky"); - LLFastTimer t(LLFastTimer::FTM_UPDATE_SKY); + LLFastTimer t(FTM_UPDATE_SKY); gSky.updateSky(); } @@ -860,7 +866,7 @@ void display(BOOL rebuild, F32 zoom_factor, int subfield, BOOL for_snapshot) LLAppViewer::instance()->pingMainloopTimeout("Display:RenderUI"); if (!for_snapshot) { - LLFastTimer t(LLFastTimer::FTM_RENDER_UI); + LLFastTimer t(FTM_RENDER_UI); render_ui(); } @@ -1040,6 +1046,7 @@ BOOL setup_hud_matrices(const LLRect& screen_region) } } +static LLFastTimer::DeclareTimer FTM_SWAP("Swap"); void render_ui(F32 zoom_factor, int subfield) { @@ -1075,7 +1082,7 @@ void render_ui(F32 zoom_factor, int subfield) gGL.color4f(1,1,1,1); if (gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI)) { - LLFastTimer t(LLFastTimer::FTM_RENDER_UI); + LLFastTimer t(FTM_RENDER_UI); if (!gDisconnected) { @@ -1102,7 +1109,7 @@ void render_ui(F32 zoom_factor, int subfield) if (gDisplaySwapBuffers) { - LLFastTimer t(LLFastTimer::FTM_SWAP); + LLFastTimer t(FTM_SWAP); gViewerWindow->mWindow->swapBuffers(); } gDisplaySwapBuffers = TRUE; diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp index c1915d2ead..0a59ba8a50 100644 --- a/indra/newview/llviewerfloaterreg.cpp +++ b/indra/newview/llviewerfloaterreg.cpp @@ -66,7 +66,7 @@ #include "llfloatergroups.h" #include "llfloaterhardwaresettings.h" #include "llfloaterhtmlcurrency.h" -#include "llfloaterhtmlhelp.h" +#include "llfloatermediabrowser.h" #include "llfloaterhud.h" #include "llfloaterimagepreview.h" #include "llimpanel.h" diff --git a/indra/newview/llviewergesture.cpp b/indra/newview/llviewergesture.cpp index 4155a87e14..93b126b67d 100644 --- a/indra/newview/llviewergesture.cpp +++ b/indra/newview/llviewergesture.cpp @@ -34,7 +34,7 @@ #include "llviewergesture.h" -#include "audioengine.h" +#include "llaudioengine.h" #include "lldir.h" #include "llviewerinventory.h" #include "sound_ids.h" // for testing diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index 78e8f084c7..4645b9bf59 100644 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -612,10 +612,7 @@ void LLViewerInventoryCategory::determineFolderType() return; if (item->getInventoryType() == LLInventoryType::IT_WEARABLE) { - U32 flags = item->getFlags(); - if (flags > WT_COUNT) - return; - const EWearableType wearable_type = EWearableType(flags); + const EWearableType wearable_type = EWearableType(item->getFlags() & LLInventoryItem::II_FLAGS_WEARABLES_MASK); const std::string& wearable_name = LLWearableDictionary::getTypeName(wearable_type); U64 valid_folder_types = LLFolderType::lookupValidFolderTypes(wearable_name); folder_valid |= valid_folder_types; @@ -1185,6 +1182,17 @@ U32 LLViewerInventoryItem::getCRC32() const return LLInventoryItem::getCRC32(); } +// This returns true if the item that this item points to +// doesn't exist in memory (i.e. LLInventoryModel). The baseitem +// might still be in the database but just not loaded yet. +bool LLViewerInventoryItem::getIsBrokenLink() const +{ + // If the item's type resolves to be a link, that means either: + // A. It wasn't able to perform indirection, i.e. the baseobj doesn't exist in memory. + // B. It's pointing to another link, which is illegal. + return LLAssetType::lookupIsLinkType(getType()); +} + const LLViewerInventoryItem *LLViewerInventoryItem::getLinkedItem() const { if (mType == LLAssetType::AT_LINK) diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index 10309d023b..8920fb053b 100644 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -141,6 +141,7 @@ public: }; LLTransactionID getTransactionID() const { return mTransactionID; } + bool getIsBrokenLink() const; // true if the baseitem this points to doesn't exist in memory. const LLViewerInventoryItem *getLinkedItem() const; const LLViewerInventoryCategory *getLinkedCategory() const; diff --git a/indra/newview/llviewerjointmesh.cpp b/indra/newview/llviewerjointmesh.cpp index 8fbb59bc74..28f883312a 100644 --- a/indra/newview/llviewerjointmesh.cpp +++ b/indra/newview/llviewerjointmesh.cpp @@ -39,7 +39,6 @@ #include "llfasttimer.h" #include "llrender.h" -#include "llagent.h" #include "llapr.h" #include "llbox.h" #include "lldrawable.h" diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp index 6bb302727d..fc2f00a2ea 100644 --- a/indra/newview/llviewerkeyboard.cpp +++ b/indra/newview/llviewerkeyboard.cpp @@ -539,7 +539,7 @@ void start_chat( EKeystate s ) void start_gesture( EKeystate s ) { if (KEYSTATE_UP == s && - !(gFocusMgr.getKeyboardFocus() && gFocusMgr.getKeyboardFocus()->acceptsTextInput())) + !(gFocusMgr.getKeyboardFocus() && dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus())->acceptsTextInput())) { if (LLNearbyChatBar::getInstance()->getCurrentChat().empty()) { diff --git a/indra/newview/llviewermedia.cpp b/indra/newview/llviewermedia.cpp index 21d686a250..c3e6bd28da 100644 --- a/indra/newview/llviewermedia.cpp +++ b/indra/newview/llviewermedia.cpp @@ -33,614 +33,1123 @@ #include "llviewerprecompiledheaders.h" #include "llviewermedia.h" - -#include "audioengine.h" - -#include "llparcel.h" - +#include "llviewermediafocus.h" +#include "llhoverview.h" #include "llmimetypes.h" #include "llviewercontrol.h" #include "llviewertexture.h" #include "llviewerparcelmedia.h" #include "llviewerparcelmgr.h" -#include "llviewerparcelmedia.h" -#include "llviewerparcelmgr.h" #include "llviewerwindow.h" #include "llversionviewer.h" #include "llviewertexturelist.h" +#include "llpluginclassmedia.h" #include "llevent.h" // LLSimpleListener -#include "llmediamanager.h" #include "lluuid.h" #include <boost/bind.hpp> // for SkinFolder listener #include <boost/signals2.hpp> +// Move this to its own file. -// Implementation functions not exported into header file -class LLViewerMediaImpl - : public LLMediaObserver +LLViewerMediaEventEmitter::~LLViewerMediaEventEmitter() { -public: - LLViewerMediaImpl() - : mMediaSource( NULL ), - mMovieImageID(), - mMovieImageHasMips(false) - { } - - void destroyMediaSource(); - - void play(const std::string& media_url, - const std::string& mime_type, - const LLUUID& placeholder_texture_id, - S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop); - - void stop(); - void pause(); - void start(); - void seek(F32 time); - void setVolume(F32 volume); - LLMediaBase::EStatus getStatus(); - - /*virtual*/ void onMediaSizeChange(const EventType& event_in); - /*virtual*/ void onMediaContentsChange(const EventType& event_in); - - void restoreMovieImage(); - void updateImagesMediaStreams(); - LLUUID getMediaTextureID(); - - // Internally set our desired browser user agent string, including - // the Second Life version and skin name. Used because we can - // switch skins without restarting the app. - static void updateBrowserUserAgent(); - - // Callback for when the SkinCurrent control is changed to - // switch the user agent string to indicate the new skin. - static bool handleSkinCurrentChanged(const LLSD& newvalue); + observerListType::iterator iter = mObservers.begin(); -public: + while( iter != mObservers.end() ) + { + LLViewerMediaObserver *self = *iter; + iter++; + remObserver(self); + } +} - // a single media url with some data and an impl. - LLMediaBase* mMediaSource; - LLUUID mMovieImageID; - bool mMovieImageHasMips; - std::string mMediaURL; - std::string mMimeType; +/////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaEventEmitter::addObserver( LLViewerMediaObserver* observer ) +{ + if ( ! observer ) + return false; -private: - void initializePlaceholderImage(LLViewerMediaTexture *placeholder_image, LLMediaBase *media_source); -}; + if ( std::find( mObservers.begin(), mObservers.end(), observer ) != mObservers.end() ) + return false; -static LLViewerMediaImpl sViewerMediaImpl; -////////////////////////////////////////////////////////////////////////////////////////// + mObservers.push_back( observer ); + observer->mEmitters.push_back( this ); -void LLViewerMediaImpl::destroyMediaSource() + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +bool LLViewerMediaEventEmitter::remObserver( LLViewerMediaObserver* observer ) +{ + if ( ! observer ) + return false; + + mObservers.remove( observer ); + observer->mEmitters.remove(this); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +void LLViewerMediaEventEmitter::emitEvent( LLPluginClassMedia* media, LLPluginClassMediaOwner::EMediaEvent event ) { - LLMediaManager* mgr = LLMediaManager::getInstance(); - if ( mMediaSource ) + observerListType::iterator iter = mObservers.begin(); + + while( iter != mObservers.end() ) { - mMediaSource->remObserver(this); - mgr->destroySource( mMediaSource ); + LLViewerMediaObserver *self = *iter; + ++iter; + self->handleMediaEvent( media, event ); + } +} - // Restore the texture - restoreMovieImage(); +// Move this to its own file. +LLViewerMediaObserver::~LLViewerMediaObserver() +{ + std::list<LLViewerMediaEventEmitter *>::iterator iter = mEmitters.begin(); + while( iter != mEmitters.end() ) + { + LLViewerMediaEventEmitter *self = *iter; + iter++; + self->remObserver( this ); } - mMediaSource = NULL; } -void LLViewerMediaImpl::play(const std::string& media_url, - const std::string& mime_type, - const LLUUID& placeholder_texture_id, - S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop) + +// Move this to its own file. +// helper class that tries to download a URL from a web site and calls a method +// on the Panel Land Media and to discover the MIME type +class LLMimeDiscoveryResponder : public LLHTTPClient::Responder { - // first stop any previously playing media - stop(); +LOG_CLASS(LLMimeDiscoveryResponder); +public: + LLMimeDiscoveryResponder( viewer_media_t media_impl) + : mMediaImpl(media_impl), + mInitialized(false) + {} + + - // Save this first, as init/load below may fire events - mMovieImageID = placeholder_texture_id; + virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content) + { + std::string media_type = content["content-type"].asString(); + std::string::size_type idx1 = media_type.find_first_of(";"); + std::string mime_type = media_type.substr(0, idx1); + completeAny(status, mime_type); + } - // If the mime_type passed in is different than the cached one, and - // Auto-discovery is turned OFF, replace the cached mime_type with the new one. - if(mime_type != mMimeType && - ! gSavedSettings.getBOOL("AutoMimeDiscovery")) + virtual void error( U32 status, const std::string& reason ) { - mMimeType = mime_type; + // completeAny(status, "none/none"); } - LLURI url(media_url); - std::string scheme = url.scheme() != "" ? url.scheme() : "http"; - LLMediaManager* mgr = LLMediaManager::getInstance(); - mMediaSource = mgr->createSourceFromMimeType(scheme, mMimeType ); - if ( !mMediaSource ) + void completeAny(U32 status, const std::string& mime_type) { - if (mMimeType != "none/none") + if(!mInitialized && ! mime_type.empty()) { - llwarns << "media source create failed " << media_url - << " type " << mMimeType - << llendl; + if (mMediaImpl->initializeMedia(mime_type)) + { + mInitialized = true; + mMediaImpl->play(); + } } - return; } - // Store the URL and Mime Type - mMediaURL = media_url; + public: + viewer_media_t mMediaImpl; + bool mInitialized; +}; +typedef std::vector<LLViewerMediaImpl*> impl_list; +static impl_list sViewerMediaImplList; + +////////////////////////////////////////////////////////////////////////////////////////// +// LLViewerMedia - if ((media_width != 0) && (media_height != 0)) +////////////////////////////////////////////////////////////////////////////////////////// +// static +viewer_media_t LLViewerMedia::newMediaImpl(const std::string& media_url, + const LLUUID& texture_id, + S32 media_width, S32 media_height, U8 media_auto_scale, + U8 media_loop, + std::string mime_type) +{ + LLViewerMediaImpl* media_impl = getMediaImplFromTextureID(texture_id); + if(media_impl == NULL || texture_id.isNull()) { - mMediaSource->setRequestedMediaSize(media_width, media_height); + // Create the media impl + media_impl = new LLViewerMediaImpl(media_url, texture_id, media_width, media_height, media_auto_scale, media_loop, mime_type); + sViewerMediaImplList.push_back(media_impl); } - - mMediaSource->setLooping(media_loop); - mMediaSource->setAutoScaled(media_auto_scale); - mMediaSource->addObserver( this ); - mMediaSource->navigateTo( media_url ); - mMediaSource->addCommand(LLMediaBase::COMMAND_START); + else + { + media_impl->stop(); + media_impl->mTextureId = texture_id; + media_impl->mMediaURL = media_url; + media_impl->mMediaWidth = media_width; + media_impl->mMediaHeight = media_height; + media_impl->mMediaAutoScale = media_auto_scale; + media_impl->mMediaLoop = media_loop; + if(! media_url.empty()) + media_impl->navigateTo(media_url, mime_type, true); + } + return media_impl; } -void LLViewerMediaImpl::stop() +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::removeMedia(LLViewerMediaImpl* media) { - destroyMediaSource(); + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) + { + if(media == *iter) + { + sViewerMediaImplList.erase(iter); + return; + } + } } -void LLViewerMediaImpl::pause() +////////////////////////////////////////////////////////////////////////////////////////// +// static +LLViewerMediaImpl* LLViewerMedia::getMediaImplFromTextureID(const LLUUID& texture_id) { - if(mMediaSource) + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) { - mMediaSource->addCommand(LLMediaBase::COMMAND_PAUSE); + LLViewerMediaImpl* media_impl = *iter; + if(media_impl->getMediaTextureID() == texture_id) + { + return media_impl; + } } + return NULL; } -void LLViewerMediaImpl::start() +////////////////////////////////////////////////////////////////////////////////////////// +// static +std::string LLViewerMedia::getCurrentUserAgent() { - if(mMediaSource) + // Don't use user-visible string to avoid + // punctuation and strange characters. + std::string skin_name = gSavedSettings.getString("SkinCurrent"); + + // Just in case we need to check browser differences in A/B test + // builds. + std::string channel = gSavedSettings.getString("VersionChannelName"); + + // append our magic version number string to the browser user agent id + // See the HTTP 1.0 and 1.1 specifications for allowed formats: + // http://www.ietf.org/rfc/rfc1945.txt section 10.15 + // http://www.ietf.org/rfc/rfc2068.txt section 3.8 + // This was also helpful: + // http://www.mozilla.org/build/revised-user-agent-strings.html + std::ostringstream codec; + codec << "SecondLife/"; + codec << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH << "." << LL_VERSION_BUILD; + codec << " (" << channel << "; " << skin_name << " skin)"; + llinfos << codec.str() << llendl; + + return codec.str(); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::updateBrowserUserAgent() +{ + std::string user_agent = getCurrentUserAgent(); + + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) { - mMediaSource->addCommand(LLMediaBase::COMMAND_START); + LLViewerMediaImpl* pimpl = *iter; + if(pimpl->mMediaSource && pimpl->mMediaSource->pluginSupportsMediaBrowser()) + { + pimpl->mMediaSource->setBrowserUserAgent(user_agent); + } } + } -void LLViewerMediaImpl::seek(F32 time) +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::handleSkinCurrentChanged(const LLSD& /*newvalue*/) { - if(mMediaSource) + // gSavedSettings is already updated when this function is called. + updateBrowserUserAgent(); + return true; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +bool LLViewerMedia::textureHasMedia(const LLUUID& texture_id) +{ + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) { - mMediaSource->seek(time); + LLViewerMediaImpl* pimpl = *iter; + if(pimpl->getMediaTextureID() == texture_id) + { + return true; + } } + return false; } -void LLViewerMediaImpl::setVolume(F32 volume) +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::setVolume(F32 volume) { - if(mMediaSource) + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) { - mMediaSource->setVolume( volume); + LLViewerMediaImpl* pimpl = *iter; + pimpl->setVolume(volume); } } -LLMediaBase::EStatus LLViewerMediaImpl::getStatus() +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::updateMedia() { - if (mMediaSource) + impl_list::iterator iter = sViewerMediaImplList.begin(); + impl_list::iterator end = sViewerMediaImplList.end(); + + for(; iter != end; iter++) { - return mMediaSource->getStatus(); + LLViewerMediaImpl* pimpl = *iter; + pimpl->update(); } - else +} + +////////////////////////////////////////////////////////////////////////////////////////// +// static +void LLViewerMedia::cleanupClass() +{ + // This is no longer necessary, since the list is no longer smart pointers. +#if 0 + while(!sViewerMediaImplList.empty()) { - return LLMediaBase::STATUS_UNKNOWN; + sViewerMediaImplList.pop_back(); } +#endif +} + +////////////////////////////////////////////////////////////////////////////////////////// +// LLViewerMediaImpl +////////////////////////////////////////////////////////////////////////////////////////// +LLViewerMediaImpl::LLViewerMediaImpl(const std::string& media_url, + const LLUUID& texture_id, + S32 media_width, + S32 media_height, + U8 media_auto_scale, + U8 media_loop, + const std::string& mime_type) +: + mMediaSource( NULL ), + mMovieImageHasMips(false), + mTextureId(texture_id), + mMediaWidth(media_width), + mMediaHeight(media_height), + mMediaAutoScale(media_auto_scale), + mMediaLoop(media_loop), + mMediaURL(media_url), + mMimeType(mime_type), + mNeedsNewTexture(true), + mSuspendUpdates(false), + mVisible(true) +{ + createMediaSource(); } ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::restoreMovieImage() +LLViewerMediaImpl::~LLViewerMediaImpl() { - // IF the media image hasn't changed, do nothing - if (mMovieImageID.isNull()) + if( gEditMenuHandler == this ) { - return; + gEditMenuHandler = NULL; } + + destroyMediaSource(); + LLViewerMedia::removeMedia(this); +} - //restore the movie image to the old one - LLViewerMediaTexture* media = LLViewerTextureManager::findMediaTexture( mMovieImageID ) ; - if (media) +////////////////////////////////////////////////////////////////////////////////////////// +bool LLViewerMediaImpl::initializeMedia(const std::string& mime_type) +{ + if((mMediaSource == NULL) || (mMimeType != mime_type)) { - if(media->getOldTexture())//set back to the old texture if it exists + if(! initializePlugin(mime_type)) { - media->switchToTexture(media->getOldTexture()) ; - media->setPlaying(FALSE) ; + LL_WARNS("Plugin") << "plugin intialization failed for mime type: " << mime_type << LL_ENDL; + LLSD args; + args["MIME_TYPE"] = mime_type; + LLNotifications::instance().add("NoPlugin", args); + + return false; } - media->reinit(mMovieImageHasMips); } - mMovieImageID.setNull(); -} + // play(); + return (mMediaSource != NULL); +} ////////////////////////////////////////////////////////////////////////////////////////// -void LLViewerMediaImpl::updateImagesMediaStreams() +void LLViewerMediaImpl::createMediaSource() { - LLMediaManager::updateClass(); + if(! mMediaURL.empty()) + { + navigateTo(mMediaURL, mMimeType, true); + } + else if(! mMimeType.empty()) + { + initializeMedia(mMimeType); + } + } -void LLViewerMediaImpl::initializePlaceholderImage(LLViewerMediaTexture *placeholder_image, LLMediaBase *media_source) +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::destroyMediaSource() { - int media_width = media_source->getMediaWidth(); - int media_height = media_source->getMediaHeight(); - //int media_rowspan = media_source->getMediaRowSpan(); - - // if width & height are invalid, don't bother doing anything - if ( media_width < 1 || media_height < 1 ) + mNeedsNewTexture = true; + if(! mMediaSource) + { return; + } + // Restore the texture + updateMovieImage(LLUUID::null, false); + delete mMediaSource; + mMediaSource = NULL; +} - llinfos << "initializing media placeholder" << llendl; - llinfos << "movie image id " << mMovieImageID << llendl; +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::setMediaType(const std::string& media_type) +{ + mMimeType = media_type; +} - int texture_width = LLMediaManager::textureWidthFromMediaWidth( media_width ); - int texture_height = LLMediaManager::textureHeightFromMediaHeight( media_height ); - int texture_depth = media_source->getMediaDepth(); +////////////////////////////////////////////////////////////////////////////////////////// +/*static*/ +LLPluginClassMedia* LLViewerMediaImpl::newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height) +{ + std::string plugin_basename = LLMIMETypes::implType(media_type); - // MEDIAOPT: check to see if size actually changed before doing work - placeholder_image->destroyGLTexture(); - // MEDIAOPT: apparently just calling setUseMipMaps(FALSE) doesn't work? - placeholder_image->reinit(FALSE); // probably not needed + if(plugin_basename.empty()) + { + LL_WARNS("Media") << "Couldn't find plugin for media type " << media_type << LL_ENDL; + } + else + { + std::string plugins_path = gDirUtilp->getLLPluginDir(); + plugins_path += gDirUtilp->getDirDelimiter(); + + std::string launcher_name = gDirUtilp->getLLPluginLauncher(); + std::string plugin_name = gDirUtilp->getLLPluginFilename(plugin_basename); + + // See if the plugin executable exists + llstat s; + if(LLFile::stat(launcher_name, &s)) + { + LL_WARNS("Media") << "Couldn't find launcher at " << launcher_name << LL_ENDL; + } + else if(LLFile::stat(plugin_name, &s)) + { + LL_WARNS("Media") << "Couldn't find plugin at " << plugin_name << LL_ENDL; + } + else + { + LLPluginClassMedia* media_source = new LLPluginClassMedia(owner); + media_source->setSize(default_width, default_height); + if (media_source->init(launcher_name, plugin_name)) + { + return media_source; + } + else + { + LL_WARNS("Media") << "Failed to init plugin. Destroying." << LL_ENDL; + delete media_source; + } + } + } + + return NULL; +} - // MEDIAOPT: seems insane that we actually have to make an imageraw then - // immediately discard it - LLPointer<LLImageRaw> raw = new LLImageRaw(texture_width, texture_height, texture_depth); - raw->clear(0x0f, 0x0f, 0x0f, 0xff); - int discard_level = 0; +////////////////////////////////////////////////////////////////////////////////////////// +bool LLViewerMediaImpl::initializePlugin(const std::string& media_type) +{ + if(mMediaSource) + { + // Save the previous media source's last set size before destroying it. + mMediaWidth = mMediaSource->getSetWidth(); + mMediaHeight = mMediaSource->getSetHeight(); + } + + // Always delete the old media impl first. + destroyMediaSource(); + + // and unconditionally set the mime type + mMimeType = media_type; - // ask media source for correct GL image format constants - placeholder_image->setExplicitFormat(media_source->getTextureFormatInternal(), - media_source->getTextureFormatPrimary(), - media_source->getTextureFormatType()); + LLPluginClassMedia* media_source = newSourceFromMediaType(media_type, this, mMediaWidth, mMediaHeight); + + if (media_source) + { + media_source->setDisableTimeout(gSavedSettings.getBOOL("DebugPluginDisableTimeout")); + media_source->setLoop(mMediaLoop); + media_source->setAutoScale(mMediaAutoScale); + media_source->setBrowserUserAgent(LLViewerMedia::getCurrentUserAgent()); + + mMediaSource = media_source; + return true; + } - placeholder_image->createGLTexture(discard_level, raw); + return false; +} - // placeholder_image->setExplicitFormat() - placeholder_image->setUseMipMaps(FALSE); +void LLViewerMediaImpl::setSize(int width, int height) +{ + mMediaWidth = width; + mMediaHeight = height; + if(mMediaSource) + { + mMediaSource->setSize(width, height); + } } -// virtual -void LLViewerMediaImpl::onMediaContentsChange(const EventType& event_in) +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::play() { - LLMediaBase* media_source = event_in.getSubject(); - LLViewerMediaTexture* placeholder_image = LLViewerTextureManager::getMediaTexture( mMovieImageID ) ; - if (placeholder_image && placeholder_image->hasValidGLTexture()) + // first stop any previously playing media + // stop(); + + // mMediaSource->addObserver( this ); + if(mMediaSource == NULL) { - if (placeholder_image->getUseMipMaps()) + if(!initializePlugin(mMimeType)) { - // bad image! NO MIPMAPS! - initializePlaceholderImage(placeholder_image, media_source); + // Plugin failed initialization... should assert or something + return; } + } + + // updateMovieImage(mTextureId, true); - U8* data = media_source->getMediaData(); - S32 x_pos = 0; - S32 y_pos = 0; - S32 width = media_source->getMediaWidth(); - S32 height = media_source->getMediaHeight(); - S32 data_width = media_source->getMediaDataWidth(); - S32 data_height = media_source->getMediaDataHeight(); - placeholder_image->setSubImage(data, data_width, data_height, - x_pos, y_pos, width, height); + mMediaSource->loadURI( mMediaURL ); + if(/*mMediaSource->pluginSupportsMediaTime()*/ true) + { + start(); } } +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::stop() +{ + if(mMediaSource) + { + mMediaSource->stop(); + // destroyMediaSource(); + } +} -// virtual -void LLViewerMediaImpl::onMediaSizeChange(const EventType& event_in) +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::pause() { - LLMediaBase* media_source = event_in.getSubject(); - LLViewerMediaTexture* placeholder_image = LLViewerTextureManager::getMediaTexture( mMovieImageID ) ; - if (placeholder_image) + if(mMediaSource) { - initializePlaceholderImage(placeholder_image, media_source); + mMediaSource->pause(); } - else +} + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::start() +{ + if(mMediaSource) { - llinfos << "no placeholder image" << llendl; + mMediaSource->start(); } } -LLUUID LLViewerMediaImpl::getMediaTextureID() +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::seek(F32 time) { - return mMovieImageID; + if(mMediaSource) + { + mMediaSource->seek(time); + } } -// static -void LLViewerMediaImpl::updateBrowserUserAgent() +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::setVolume(F32 volume) { - // Don't use user-visible string to avoid - // punctuation and strange characters. - std::string skin_name = gSavedSettings.getString("SkinCurrent"); + if(mMediaSource) + { + mMediaSource->setVolume(volume); + } +} - // Just in case we need to check browser differences in A/B test - // builds. - std::string channel = gSavedSettings.getString("VersionChannelName"); +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::focus(bool focus) +{ + if (mMediaSource) + { + // call focus just for the hell of it, even though this apopears to be a nop + mMediaSource->focus(focus); + if (focus) + { + // spoof a mouse click to *actually* pass focus + // Don't do this anymore -- it actually clicks through now. +// mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOWN, 1, 1, 0); +// mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, 1, 1, 0); + } + } +} - // append our magic version number string to the browser user agent id - // See the HTTP 1.0 and 1.1 specifications for allowed formats: - // http://www.ietf.org/rfc/rfc1945.txt section 10.15 - // http://www.ietf.org/rfc/rfc2068.txt section 3.8 - // This was also helpful: - // http://www.mozilla.org/build/revised-user-agent-strings.html - std::ostringstream codec; - codec << "SecondLife/"; - codec << LL_VERSION_MAJOR << "." << LL_VERSION_MINOR << "." << LL_VERSION_PATCH << "." << LL_VERSION_BUILD; - codec << " (" << channel << "; " << skin_name << " skin)"; - llinfos << codec.str() << llendl; - LLMediaManager::setBrowserUserAgent( codec.str() ); +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::mouseDown(S32 x, S32 y) +{ + scaleMouse(&x, &y); + mLastMouseX = x; + mLastMouseY = y; + if (mMediaSource) + { + mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOWN, x, y, 0); + } } -// static -bool LLViewerMediaImpl::handleSkinCurrentChanged(const LLSD& /*newvalue*/) +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::mouseUp(S32 x, S32 y) { - // gSavedSettings is already updated when this function is called. - updateBrowserUserAgent(); - return true; + scaleMouse(&x, &y); + mLastMouseX = x; + mLastMouseY = y; + if (mMediaSource) + { + mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, x, y, 0); + } } ////////////////////////////////////////////////////////////////////////////////////////// -// Wrapper class +void LLViewerMediaImpl::mouseMove(S32 x, S32 y) +{ + scaleMouse(&x, &y); + mLastMouseX = x; + mLastMouseY = y; + if (mMediaSource) + { + mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_MOVE, x, y, 0); + } +} + ////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::mouseLeftDoubleClick(S32 x, S32 y) +{ + scaleMouse(&x, &y); + mLastMouseX = x; + mLastMouseY = y; + if (mMediaSource) + { + mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_DOUBLE_CLICK, x, y, 0); + } +} -S32 LLViewerMedia::mMusicState = LLViewerMedia::STOPPED; ////////////////////////////////////////////////////////////////////////////////////////// -// The viewer takes a long time to load the start screen. Part of the problem -// is media initialization -- in particular, QuickTime loads many DLLs and -// hits the disk heavily. So we initialize only the browser component before -// the login screen, then do the rest later when we have a progress bar. JC -// static -void LLViewerMedia::initBrowser() +void LLViewerMediaImpl::onMouseCaptureLost() { - LLMediaManagerData* init_data = new LLMediaManagerData; - buildMediaManagerData( init_data ); - LLMediaManager::initBrowser( init_data ); - delete init_data; + if (mMediaSource) + { + mMediaSource->mouseEvent(LLPluginClassMedia::MOUSE_EVENT_UP, mLastMouseX, mLastMouseY, 0); + } +} + +////////////////////////////////////////////////////////////////////////////////////////// +BOOL LLViewerMediaImpl::handleMouseUp(S32 x, S32 y, MASK mask) +{ + // NOTE: this is called when the mouse is released when we have capture. + // Due to the way mouse coordinates are mapped to the object, we can't use the x and y coordinates that come in with the event. + + if(hasMouseCapture()) + { + // Release the mouse -- this will also send a mouseup to the media + gFocusMgr.setMouseCapture( FALSE ); + } - // We use a custom user agent with viewer version and skin name. - LLViewerMediaImpl::updateBrowserUserAgent(); + return TRUE; +} +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateHome() +{ + if(mMediaSource) + { + mMediaSource->loadURI( mHomeURL ); + } } ////////////////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerMedia::initClass() +void LLViewerMediaImpl::navigateTo(const std::string& url, const std::string& mime_type, bool rediscover_type) { - // *TODO: This looks like a memory leak to me. JC - LLMediaManagerData* init_data = new LLMediaManagerData; - buildMediaManagerData( init_data ); - LLMediaManager::initClass( init_data ); - delete init_data; + if(rediscover_type) + { + + LLURI uri(url); + std::string scheme = uri.scheme(); + + if(scheme.empty() || "http" == scheme || "https" == scheme) + { + LLHTTPClient::getHeaderOnly( url, new LLMimeDiscoveryResponder(this)); + } + else if("data" == scheme || "file" == scheme || "about" == scheme) + { + // FIXME: figure out how to really discover the type for these schemes + // We use "data" internally for a text/html url for loading the login screen + if(initializeMedia("text/html")) + { + mMediaSource->loadURI( url ); + } + } + else + { + // This catches 'rtsp://' urls + if(initializeMedia(scheme)) + { + mMediaSource->loadURI( url ); + } + } + } + else if (mMediaSource) + { + mMediaSource->loadURI( url ); + } + else if(initializeMedia(mime_type) && mMediaSource) + { + mMediaSource->loadURI( url ); + } + else + { + LL_WARNS("Media") << "Couldn't navigate to: " << url << " as there is no media type for: " << mime_type << LL_ENDL; + return; + } + mMediaURL = url; + +} - LLMediaManager* mm = LLMediaManager::getInstance(); - LLMIMETypes::mime_info_map_t::const_iterator it; - for (it = LLMIMETypes::sMap.begin(); it != LLMIMETypes::sMap.end(); ++it) +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::navigateStop() +{ + if(mMediaSource) { - const std::string& mime_type = it->first; - const LLMIMETypes::LLMIMEInfo& info = it->second; - mm->addMimeTypeImplNameMap( mime_type, info.mImpl ); + mMediaSource->browse_stop(); } + } ////////////////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerMedia::buildMediaManagerData( LLMediaManagerData* init_data ) -{ -// std::string executable_dir = std::string( arg0 ).substr( 0, std::string( arg0 ).find_last_of("\\/") ); -// std::string component_dir = std::string( executable_dir ).substr( 0, std::string( executable_dir ).find_last_of("\\/") ); -// component_dir = std::string( component_dir ).substr( 0, std::string( component_dir ).find_last_of("\\/") ); -// component_dir = std::string( component_dir ).substr( 0, std::string( component_dir ).find_last_of("\\/") ); -// component_dir += "\\newview\\app_settings\\mozilla"; - - -#if LL_DARWIN - // For Mac OS, we store both the shared libraries and the runtime files (chrome/, plugins/, etc) in - // Second Life.app/Contents/MacOS/. This matches the way Firefox is distributed on the Mac. - std::string component_dir(gDirUtilp->getExecutableDir()); -#elif LL_WINDOWS - std::string component_dir( gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "" ) ); - component_dir += gDirUtilp->getDirDelimiter(); - #ifdef LL_DEBUG - component_dir += "mozilla_debug"; +bool LLViewerMediaImpl::handleKeyHere(KEY key, MASK mask) +{ + bool result = false; // *NOTE:Mani - if this doesn't exist llmozlib goes crashy in the debug build. // LLMozlib::init wants to write some files to <exe_dir>/components std::string debug_init_component_dir( gDirUtilp->getExecutableDir() ); debug_init_component_dir += "/components"; LLAPRFile::makeDir(debug_init_component_dir.c_str()); - #else // LL_DEBUG - component_dir += "mozilla"; - #endif // LL_DEBUG -#elif LL_LINUX - std::string component_dir( gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "" ) ); - component_dir += gDirUtilp->getDirDelimiter(); - component_dir += "mozilla-runtime-linux-i686"; -#elif LL_SOLARIS - std::string component_dir( gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "" ) ); - component_dir += gDirUtilp->getDirDelimiter(); - #ifdef __sparc - component_dir += "mozilla-solaris-sparc"; - #else - component_dir += "mozilla-solaris-i686"; - #endif -#else - std::string component_dir( gDirUtilp->getExpandedFilename( LL_PATH_APP_SETTINGS, "" ) ); - component_dir += gDirUtilp->getDirDelimiter(); - component_dir += "mozilla"; -#endif - - std::string application_dir = gDirUtilp->getExecutableDir(); - - init_data->setBrowserApplicationDir( application_dir ); - std::string profile_dir = gDirUtilp->getExpandedFilename( LL_PATH_MOZILLA_PROFILE, "" ); - init_data->setBrowserProfileDir( profile_dir ); - init_data->setBrowserComponentDir( component_dir ); - std::string profile_name("Second Life"); - init_data->setBrowserProfileName( profile_name ); - init_data->setBrowserParentWindow( gViewerWindow->getMediaWindow() ); - - // Users can change skins while client is running, so make sure - // we pick up on changes. - gSavedSettings.getControl("SkinCurrent")->getSignal()->connect( - boost::bind( LLViewerMediaImpl::handleSkinCurrentChanged, _2 ) ); - + + if (mMediaSource) + { + result = mMediaSource->keyEvent(LLPluginClassMedia::KEY_EVENT_DOWN ,key, mask); + } + + return result; } ////////////////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerMedia::cleanupClass() +bool LLViewerMediaImpl::handleUnicodeCharHere(llwchar uni_char) { - stop() ; - LLMediaManager::cleanupClass(); + bool result = false; + + if (mMediaSource) + { + mMediaSource->textInput(wstring_to_utf8str(LLWString(1, uni_char))); + } + + return result; } -// static -void LLViewerMedia::play(const std::string& media_url, - const std::string& mime_type, - const LLUUID& placeholder_texture_id, - S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop) +////////////////////////////////////////////////////////////////////////////////////////// +bool LLViewerMediaImpl::canNavigateForward() { - sViewerMediaImpl.play(media_url, mime_type, placeholder_texture_id, - media_width, media_height, media_auto_scale, media_loop); + BOOL result = FALSE; + if (mMediaSource) + { + result = mMediaSource->getHistoryForwardAvailable(); + } + return result; } -// static -void LLViewerMedia::stop() +////////////////////////////////////////////////////////////////////////////////////////// +bool LLViewerMediaImpl::canNavigateBack() { - sViewerMediaImpl.stop(); + BOOL result = FALSE; + if (mMediaSource) + { + result = mMediaSource->getHistoryBackAvailable(); + } + return result; } -// static -void LLViewerMedia::pause() -{ - sViewerMediaImpl.pause(); -} -// static -void LLViewerMedia::start() +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::updateMovieImage(const LLUUID& uuid, BOOL active) { - sViewerMediaImpl.start(); + // IF the media image hasn't changed, do nothing + if (mTextureId == uuid) + { + return; + } + // If we have changed media uuid, restore the old one + if (!mTextureId.isNull()) + { + LLViewerMediaTexture* old_image = LLViewerTextureManager::findMediaTexture( mTextureId ); + if (old_image) + { + old_image->setPlaying(FALSE); + LLViewerTexture* original_texture = old_image->getOldTexture(); + if(original_texture) + { + old_image->switchToTexture(original_texture); + } + } + } + // If the movie is playing, set the new media image + if (active && !uuid.isNull()) + { + LLViewerMediaTexture* viewerImage = LLViewerTextureManager::findMediaTexture( uuid ); + if( viewerImage ) + { + mTextureId = uuid; + + // Can't use mipmaps for movies because they don't update the full image + mMovieImageHasMips = viewerImage->getUseMipMaps(); + viewerImage->reinit(FALSE); + // FIXME +// viewerImage->mIsMediaTexture = TRUE; + } + } } -// static -void LLViewerMedia::seek(F32 time) +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::update() { - sViewerMediaImpl.seek(time); + if(mMediaSource == NULL) + { + return; + } + + mMediaSource->idle(); + + if(mMediaSource->isPluginExited()) + { + destroyMediaSource(); + return; + } + + if(!mMediaSource->textureValid()) + { + return; + } + + if(mSuspendUpdates || !mVisible) + { + return; + } + + LLViewerMediaTexture* placeholder_image = updatePlaceholderImage(); + + if(placeholder_image) + { + LLRect dirty_rect; + if(mMediaSource->getDirty(&dirty_rect)) + { + // Constrain the dirty rect to be inside the texture + S32 x_pos = llmax(dirty_rect.mLeft, 0); + S32 y_pos = llmax(dirty_rect.mBottom, 0); + S32 width = llmin(dirty_rect.mRight, placeholder_image->getWidth()) - x_pos; + S32 height = llmin(dirty_rect.mTop, placeholder_image->getHeight()) - y_pos; + + if(width > 0 && height > 0) + { + + U8* data = mMediaSource->getBitsData(); + + // Offset the pixels pointer to match x_pos and y_pos + data += ( x_pos * mMediaSource->getTextureDepth() * mMediaSource->getBitsWidth() ); + data += ( y_pos * mMediaSource->getTextureDepth() ); + + placeholder_image->setSubImage( + data, + mMediaSource->getBitsWidth(), + mMediaSource->getBitsHeight(), + x_pos, + y_pos, + width, + height); + + } + + mMediaSource->resetDirty(); + } + } } -// static -void LLViewerMedia::setVolume(F32 volume) + +////////////////////////////////////////////////////////////////////////////////////////// +void LLViewerMediaImpl::updateImagesMediaStreams() { - sViewerMediaImpl.setVolume(volume); } -// static -LLMediaBase::EStatus LLViewerMedia::getStatus() + +////////////////////////////////////////////////////////////////////////////////////////// +LLViewerMediaTexture* LLViewerMediaImpl::updatePlaceholderImage() { - return sViewerMediaImpl.getStatus(); + if(mTextureId.isNull()) + { + // The code that created this instance will read from the plugin's bits. + return NULL; + } + + LLViewerMediaTexture* placeholder_image = LLViewerTextureManager::getMediaTexture( mTextureId ); + + if (mNeedsNewTexture + || placeholder_image->getUseMipMaps() +// || ! placeholder_image->getType() == LLViewerTexture::MEDIA_TEXTURE + || placeholder_image->getWidth() != mMediaSource->getTextureWidth() + || placeholder_image->getHeight() != mMediaSource->getTextureHeight()) + { + llinfos << "initializing media placeholder" << llendl; + llinfos << "movie image id " << mTextureId << llendl; + + int texture_width = mMediaSource->getTextureWidth(); + int texture_height = mMediaSource->getTextureHeight(); + int texture_depth = mMediaSource->getTextureDepth(); + + // MEDIAOPT: check to see if size actually changed before doing work + placeholder_image->destroyGLTexture(); + // MEDIAOPT: apparently just calling setUseMipMaps(FALSE) doesn't work? + placeholder_image->reinit(FALSE); // probably not needed + + // MEDIAOPT: seems insane that we actually have to make an imageraw then + // immediately discard it + LLPointer<LLImageRaw> raw = new LLImageRaw(texture_width, texture_height, texture_depth); + raw->clear(0x0f, 0x0f, 0x0f, 0xff); + int discard_level = 0; + + // ask media source for correct GL image format constants + placeholder_image->setExplicitFormat(mMediaSource->getTextureFormatInternal(), + mMediaSource->getTextureFormatPrimary(), + mMediaSource->getTextureFormatType(), + mMediaSource->getTextureFormatSwapBytes()); + + placeholder_image->createGLTexture(discard_level, raw); + + // placeholder_image->setExplicitFormat() + placeholder_image->setUseMipMaps(FALSE); + + // MEDIAOPT: set this dynamically on play/stop + // FIXME +// placeholder_image->mIsMediaTexture = true; + mNeedsNewTexture = false; + } + + return placeholder_image; } + ////////////////////////////////////////////////////////////////////////////////////////// -// static -LLUUID LLViewerMedia::getMediaTextureID() +LLUUID LLViewerMediaImpl::getMediaTextureID() { - return sViewerMediaImpl.getMediaTextureID(); + return mTextureId; } ////////////////////////////////////////////////////////////////////////////////////////// -// static -bool LLViewerMedia::getMediaSize(S32 *media_width, S32 *media_height) +void LLViewerMediaImpl::setVisible(bool visible) { - // make sure we're valid - - if ( sViewerMediaImpl.mMediaSource != NULL ) + mVisible = visible; + + if(mVisible) { - *media_width = sViewerMediaImpl.mMediaSource->getMediaWidth(); - *media_height = sViewerMediaImpl.mMediaSource->getMediaHeight(); - return true; + if(mMediaSource && mMediaSource->isPluginExited()) + { + destroyMediaSource(); + } + + if(!mMediaSource) + { + createMediaSource(); + } + } + + if(mMediaSource) + { + mMediaSource->setPriority(mVisible?LLPluginClassMedia::PRIORITY_NORMAL:LLPluginClassMedia::PRIORITY_HIDDEN); } - return false; } ////////////////////////////////////////////////////////////////////////////////////////// -// static -bool LLViewerMedia::getTextureSize(S32 *texture_width, S32 *texture_height) +void LLViewerMediaImpl::mouseCapture() { - if ( sViewerMediaImpl.mMediaSource != NULL ) - { - S32 media_width = sViewerMediaImpl.mMediaSource->getMediaWidth(); - S32 media_height = sViewerMediaImpl.mMediaSource->getMediaHeight(); - *texture_width = LLMediaManager::textureWidthFromMediaWidth( media_width ); - *texture_height = LLMediaManager::textureHeightFromMediaHeight( media_height ); - return true; - } - return false; + gFocusMgr.setMouseCapture(this); } - ////////////////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerMedia::updateImagesMediaStreams() +void LLViewerMediaImpl::scaleMouse(S32 *mouse_x, S32 *mouse_y) { - sViewerMediaImpl.updateImagesMediaStreams(); +#if 0 + S32 media_width, media_height; + S32 texture_width, texture_height; + getMediaSize( &media_width, &media_height ); + getTextureSize( &texture_width, &texture_height ); + S32 y_delta = texture_height - media_height; + + *mouse_y -= y_delta; +#endif } + ////////////////////////////////////////////////////////////////////////////////////////// -// static -bool LLViewerMedia::isMediaPlaying() +bool LLViewerMediaImpl::isMediaPlaying() { - LLMediaBase::EStatus status = sViewerMediaImpl.getStatus(); - return (status == LLMediaBase::STATUS_STARTED ); + bool result = false; + + if(mMediaSource) + { + EMediaStatus status = mMediaSource->getStatus(); + if(status == MEDIA_PLAYING || status == MEDIA_LOADING) + result = true; + } + + return result; } ////////////////////////////////////////////////////////////////////////////////////////// -// static -bool LLViewerMedia::isMediaPaused() +bool LLViewerMediaImpl::isMediaPaused() { - LLMediaBase::EStatus status = sViewerMediaImpl.getStatus(); - return (status == LLMediaBase::STATUS_PAUSED); + bool result = false; + + if(mMediaSource) + { + if(mMediaSource->getStatus() == MEDIA_PAUSED) + result = true; + } + + return result; } + ////////////////////////////////////////////////////////////////////////////////////////// -// static -bool LLViewerMedia::hasMedia() +// +bool LLViewerMediaImpl::hasMedia() { - return sViewerMediaImpl.mMediaSource != NULL; + return mMediaSource != NULL; } ////////////////////////////////////////////////////////////////////////////////////////// -//static -bool LLViewerMedia::isActiveMediaTexture(const LLUUID& id) +void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent event) { - return (id.notNull() - && id == getMediaTextureID() - && isMediaPlaying()); + switch(event) + { + case MEDIA_EVENT_PLUGIN_FAILED: + { + LLSD args; + args["PLUGIN"] = LLMIMETypes::implType(mMimeType); + LLNotifications::instance().add("MediaPluginFailed", args); + } + break; + default: + break; + } + // Just chain the event to observers. + emitEvent(self, event); } -////////////////////////////////////////////////////////////////////////////////////////// -//static -bool LLViewerMedia::isMusicPlaying() +//////////////////////////////////////////////////////////////////////////////// +// virtual +void +LLViewerMediaImpl::cut() { - return mMusicState == PLAYING; + if (mMediaSource) + mMediaSource->cut(); } -////////////////////////////////////////////////////////////////////////////////////////// -// static -std::string LLViewerMedia::getMediaURL() +//////////////////////////////////////////////////////////////////////////////// +// virtual +BOOL +LLViewerMediaImpl::canCut() const +{ + if (mMediaSource) + return mMediaSource->canCut(); + else + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +void +LLViewerMediaImpl::copy() { - return sViewerMediaImpl.mMediaURL; + if (mMediaSource) + mMediaSource->copy(); } -////////////////////////////////////////////////////////////////////////////////////////// -// static -std::string LLViewerMedia::getMimeType() + +//////////////////////////////////////////////////////////////////////////////// +// virtual +BOOL +LLViewerMediaImpl::canCopy() const { - return sViewerMediaImpl.mMimeType; + if (mMediaSource) + return mMediaSource->canCopy(); + else + return FALSE; } -////////////////////////////////////////////////////////////////////////////////////////// -// static -void LLViewerMedia::setMimeType(std::string mime_type) + +//////////////////////////////////////////////////////////////////////////////// +// virtual +void +LLViewerMediaImpl::paste() { - sViewerMediaImpl.mMimeType = mime_type; + if (mMediaSource) + mMediaSource->paste(); +} + +//////////////////////////////////////////////////////////////////////////////// +// virtual +BOOL +LLViewerMediaImpl::canPaste() const +{ + if (mMediaSource) + return mMediaSource->canPaste(); + else + return FALSE; } + ////////////////////////////////////////////////////////////////////////////////////////// //static void LLViewerMedia::toggleMusicPlay(void*) { +// FIXME: This probably doesn't belong here +#if 0 if (mMusicState != PLAYING) { mMusicState = PLAYING; // desired state @@ -661,12 +1170,15 @@ void LLViewerMedia::toggleMusicPlay(void*) gAudiop->stopInternetStream(); } } +#endif } ////////////////////////////////////////////////////////////////////////////////////////// //static void LLViewerMedia::toggleMediaPlay(void*) { +// FIXME: This probably doesn't belong here +#if 0 if (LLViewerMedia::isMediaPaused()) { LLViewerParcelMedia::start(); @@ -683,11 +1195,24 @@ void LLViewerMedia::toggleMediaPlay(void*) LLViewerParcelMedia::play(parcel); } } +#endif } ////////////////////////////////////////////////////////////////////////////////////////// //static void LLViewerMedia::mediaStop(void*) { +// FIXME: This probably doesn't belong here +#if 0 LLViewerParcelMedia::stop(); +#endif +} + +////////////////////////////////////////////////////////////////////////////////////////// +//static +bool LLViewerMedia::isMusicPlaying() +{ +// FIXME: This probably doesn't belong here +// FIXME: make this work + return false; } diff --git a/indra/newview/llviewermedia.h b/indra/newview/llviewermedia.h index b3fb2c9031..68a49662e7 100644 --- a/indra/newview/llviewermedia.h +++ b/indra/newview/llviewermedia.h @@ -33,58 +33,196 @@ #ifndef LLVIEWERMEDIA_H #define LLVIEWERMEDIA_H -#include "llmediabase.h" // for status codes +#include "llfocusmgr.h" +#include "lleditmenuhandler.h" -class LLMediaManagerData; +#include "llpanel.h" +#include "llpluginclassmediaowner.h" + +#include "llviewermediaobserver.h" + +class LLViewerMediaImpl; class LLUUID; +class LLViewerMediaTexture; + +typedef LLPointer<LLViewerMediaImpl> viewer_media_t; +/////////////////////////////////////////////////////////////////////////////// +// +class LLViewerMediaEventEmitter +{ +public: + virtual ~LLViewerMediaEventEmitter(); + + bool addObserver( LLViewerMediaObserver* subject ); + bool remObserver( LLViewerMediaObserver* subject ); + void emitEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent event); + +private: + typedef std::list< LLViewerMediaObserver* > observerListType; + observerListType mObservers; +}; class LLViewerMedia { + LOG_CLASS(LLViewerMedia); public: // Special case early init for just web browser component // so we can show login screen. See .cpp file for details. JC - static void initBrowser(); - static void initClass(); - static void cleanupClass(); + static viewer_media_t newMediaImpl(const std::string& media_url, + const LLUUID& texture_id, + S32 media_width, + S32 media_height, + U8 media_auto_scale, + U8 media_loop, + std::string mime_type = "none/none"); - static void play(const std::string& media_url, - const std::string& mime_type, - const LLUUID& placeholder_texture_id, - S32 media_width, S32 media_height, U8 media_auto_scale, - U8 media_loop); - static void stop(); - static void pause(); - static void start(); - static void seek(F32 time); + static void removeMedia(LLViewerMediaImpl* media); + static LLViewerMediaImpl* getMediaImplFromTextureID(const LLUUID& texture_id); + static std::string getCurrentUserAgent(); + static void updateBrowserUserAgent(); + static bool handleSkinCurrentChanged(const LLSD& /*newvalue*/); + static bool textureHasMedia(const LLUUID& texture_id); static void setVolume(F32 volume); - static LLMediaBase::EStatus getStatus(); - - static LLUUID getMediaTextureID(); - static bool getMediaSize(S32 *media_width, S32 *media_height); - static bool getTextureSize(S32 *texture_width, S32 *texture_height); - static bool isMediaPlaying(); - static bool isMediaPaused(); - static bool hasMedia(); - static bool isActiveMediaTexture(const LLUUID& id); - static bool isMusicPlaying(); - static std::string getMediaURL(); - static std::string getMimeType(); - static void setMimeType(std::string mime_type); + static void updateMedia(); + static bool isMusicPlaying(); - static void updateImagesMediaStreams(); + static void cleanupClass(); static void toggleMusicPlay(void*); static void toggleMediaPlay(void*); static void mediaStop(void*); +}; + +// Implementation functions not exported into header file +class LLViewerMediaImpl + : public LLMouseHandler, public LLRefCount, public LLPluginClassMediaOwner, public LLViewerMediaEventEmitter, public LLEditMenuHandler +{ + LOG_CLASS(LLViewerMediaImpl); +public: + + LLViewerMediaImpl(const std::string& media_url, + const LLUUID& texture_id, + S32 media_width, + S32 media_height, + U8 media_auto_scale, + U8 media_loop, + const std::string& mime_type); + + ~LLViewerMediaImpl(); + void createMediaSource(); + void destroyMediaSource(); + void setMediaType(const std::string& media_type); + bool initializeMedia(const std::string& mime_type); + bool initializePlugin(const std::string& media_type); + LLPluginClassMedia* getMediaPlugin() { return mMediaSource; } + void setSize(int width, int height); + + void play(); + void stop(); + void pause(); + void start(); + void seek(F32 time); + void setVolume(F32 volume); + void focus(bool focus); + void mouseDown(S32 x, S32 y); + void mouseUp(S32 x, S32 y); + void mouseMove(S32 x, S32 y); + void mouseLeftDoubleClick(S32 x,S32 y ); + void mouseCapture(); + + void navigateHome(); + void navigateTo(const std::string& url, const std::string& mime_type = "", bool rediscover_type = false); + void navigateStop(); + bool handleKeyHere(KEY key, MASK mask); + bool handleUnicodeCharHere(llwchar uni_char); + bool canNavigateForward(); + bool canNavigateBack(); + std::string getMediaURL() { return mMediaURL; } + std::string getMediaHomeURL() { return mHomeURL; } + std::string getMimeType() { return mMimeType; } + void scaleMouse(S32 *mouse_x, S32 *mouse_y); + + void update(); + void updateMovieImage(const LLUUID& image_id, BOOL active); + void updateImagesMediaStreams(); + LLUUID getMediaTextureID(); + + void suspendUpdates(bool suspend) { mSuspendUpdates = suspend; }; + void setVisible(bool visible); + + bool isMediaPlaying(); + bool isMediaPaused(); + bool hasMedia(); + + // utility function to create a ready-to-use media instance from a desired media type. + static LLPluginClassMedia* newSourceFromMediaType(std::string media_type, LLPluginClassMediaOwner *owner /* may be NULL */, S32 default_width, S32 default_height); + + // Internally set our desired browser user agent string, including + // the Second Life version and skin name. Used because we can + // switch skins without restarting the app. + static void updateBrowserUserAgent(); + + // Callback for when the SkinCurrent control is changed to + // switch the user agent string to indicate the new skin. + static bool handleSkinCurrentChanged(const LLSD& newvalue); + + // need these to handle mouseup... + /*virtual*/ void onMouseCaptureLost(); + /*virtual*/ BOOL handleMouseUp(S32 x, S32 y, MASK mask); + + // Grr... the only thing I want as an LLMouseHandler are the onMouseCaptureLost and handleMouseUp calls. + // Sadly, these are all pure virtual, so I have to supply implementations here: + /*virtual*/ BOOL handleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }; + /*virtual*/ BOOL handleHover(S32 x, S32 y, MASK mask) { return FALSE; }; + /*virtual*/ BOOL handleScrollWheel(S32 x, S32 y, S32 clicks) { return FALSE; }; + /*virtual*/ BOOL handleDoubleClick(S32 x, S32 y, MASK mask) { return FALSE; }; + /*virtual*/ BOOL handleRightMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }; + /*virtual*/ BOOL handleRightMouseUp(S32 x, S32 y, MASK mask) { return FALSE; }; + /*virtual*/ BOOL handleToolTip(S32 x, S32 y, std::string& msg, LLRect* sticky_rect_screen) { return FALSE; }; + /*virtual*/ BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask) { return FALSE; }; + /*virtual*/ BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask) {return FALSE; }; + /*virtual*/ const std::string& getName() const { return LLStringUtil::null; }; + /*virtual*/ BOOL isView() const { return FALSE; }; + /*virtual*/ void screenPointToLocal(S32 screen_x, S32 screen_y, S32* local_x, S32* local_y) const {}; + /*virtual*/ void localPointToScreen(S32 local_x, S32 local_y, S32* screen_x, S32* screen_y) const {}; + /*virtual*/ BOOL hasMouseCapture() { return gFocusMgr.getMouseCapture() == this; }; + + // Inherited from LLPluginClassMediaOwner + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent); + + // LLEditMenuHandler overrides + /*virtual*/ void cut(); + /*virtual*/ BOOL canCut() const; + + /*virtual*/ void copy(); + /*virtual*/ BOOL canCopy() const; + + /*virtual*/ void paste(); + /*virtual*/ BOOL canPaste() const; + +public: + // a single media url with some data and an impl. + LLPluginClassMedia* mMediaSource; + LLUUID mTextureId; + bool mMovieImageHasMips; + std::string mMediaURL; + std::string mHomeURL; + std::string mMimeType; + S32 mLastMouseX; // save the last mouse coord we get, so when we lose capture we can simulate a mouseup at that point. + S32 mLastMouseY; + S32 mMediaWidth; + S32 mMediaHeight; + bool mMediaAutoScale; + bool mMediaLoop; + bool mNeedsNewTexture; + bool mSuspendUpdates; + bool mVisible; + - private: - // Fill in initialization data for LLMediaManager::initClass() - static void buildMediaManagerData( LLMediaManagerData* init_data ); - - enum { STOPPED=0, PLAYING=1, PAUSED=2 }; - static S32 mMusicState; +private: + LLViewerMediaTexture *updatePlaceholderImage(); }; #endif // LLVIEWERMEDIA_H diff --git a/indra/newview/llviewermedia_streamingaudio.cpp b/indra/newview/llviewermedia_streamingaudio.cpp new file mode 100644 index 0000000000..90cfb85821 --- /dev/null +++ b/indra/newview/llviewermedia_streamingaudio.cpp @@ -0,0 +1,167 @@ +/** + * @file llviewermedia_streamingaudio.h + * @author Tofu Linden, Sam Kolb + * @brief LLStreamingAudio_MediaPlugins implementation - an implementation of the streaming audio interface which is implemented as a client of the media plugins API. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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 "llviewerprecompiledheaders.h" +#include "linden_common.h" +#include "llpluginclassmedia.h" +#include "llviewermedia.h" + +#include "llviewermedia_streamingaudio.h" + +#include "llmimetypes.h" +#include "llvfs.h" +#include "lldir.h" + + +LLStreamingAudio_MediaPlugins::LLStreamingAudio_MediaPlugins() : + mMediaPlugin(NULL), + mGain(1.0) +{ + // nothing interesting to do? + // we will lazily create a media plugin at play-time, if none exists. +} + +LLStreamingAudio_MediaPlugins::~LLStreamingAudio_MediaPlugins() +{ + delete mMediaPlugin; + mMediaPlugin = NULL; +} + +void LLStreamingAudio_MediaPlugins::start(const std::string& url) +{ + if (!mMediaPlugin) // lazy-init the underlying media plugin + { + mMediaPlugin = initializeMedia("audio/mpeg"); // assumes that whatever media implementation supports mp3 also supports vorbis. + llinfos << "mMediaPlugin is now " << mMediaPlugin << llendl; + } + + if(!mMediaPlugin) + return; + + if (!url.empty()) { + llinfos << "Starting internet stream: " << url << llendl; + mURL = url; + mMediaPlugin->loadURI ( url ); + mMediaPlugin->start(); + llinfos << "Playing....." << llendl; + } else { + llinfos << "setting stream to NULL"<< llendl; + mURL.clear(); + mMediaPlugin->stop(); + } +} + +void LLStreamingAudio_MediaPlugins::stop() +{ + if(mMediaPlugin) + { + mMediaPlugin->stop(); + } + + mURL.clear(); +} + +void LLStreamingAudio_MediaPlugins::pause(int pause) +{ + if(!mMediaPlugin) + return; + + if(pause) + { + mMediaPlugin->pause(); + } + else + { + mMediaPlugin->start(); + } +} + +void LLStreamingAudio_MediaPlugins::update() +{ + if (mMediaPlugin) + mMediaPlugin->idle(); +} + +int LLStreamingAudio_MediaPlugins::isPlaying() +{ + if (!mMediaPlugin) + return 0; + + // *TODO: can probably do better than this + if (mMediaPlugin->isPluginRunning()) + { + return 1; // Active and playing + } + + if (mMediaPlugin->isPluginExited()) + { + return 0; // stopped + } + + return 2; // paused +} + +void LLStreamingAudio_MediaPlugins::setGain(F32 vol) +{ + mGain = vol; + + if(!mMediaPlugin) + return; + + vol = llclamp(vol, 0.f, 1.f); + mMediaPlugin->setVolume(vol); +} + +F32 LLStreamingAudio_MediaPlugins::getGain() +{ + return mGain; +} + +std::string LLStreamingAudio_MediaPlugins::getURL() +{ + return mURL; +} + +LLPluginClassMedia* LLStreamingAudio_MediaPlugins::initializeMedia(const std::string& media_type) +{ + LLPluginClassMediaOwner* owner = NULL; + S32 default_size = 1; // audio-only - be minimal, doesn't matter + LLPluginClassMedia* media_source = LLViewerMediaImpl::newSourceFromMediaType(media_type, owner, default_size, default_size); + + if (media_source) + { + media_source->setLoop(false); // audio streams are not expected to loop + } + + return media_source; +} + diff --git a/indra/newview/llviewermedia_streamingaudio.h b/indra/newview/llviewermedia_streamingaudio.h new file mode 100644 index 0000000000..270bab7625 --- /dev/null +++ b/indra/newview/llviewermedia_streamingaudio.h @@ -0,0 +1,69 @@ +/** + * @file llviewermedia_streamingaudio.h + * @author Tofu Linden + * @brief Definition of LLStreamingAudio_MediaPlugins implementation - an implementation of the streaming audio interface which is implemented as a client of the media plugins API. + * + * $LicenseInfo:firstyear=2009&license=viewergpl$ + * + * Copyright (c) 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_VIEWERMEDIA_STREAMINGAUDIO_H +#define LL_VIEWERMEDIA_STREAMINGAUDIO_H + + +#include "stdtypes.h" // from llcommon + +#include "llstreamingaudio.h" + +class LLPluginClassMedia; + +class LLStreamingAudio_MediaPlugins : public LLStreamingAudioInterface +{ + public: + LLStreamingAudio_MediaPlugins(); + /*virtual*/ ~LLStreamingAudio_MediaPlugins(); + + /*virtual*/ void start(const std::string& url); + /*virtual*/ void stop(); + /*virtual*/ void pause(int pause); + /*virtual*/ void update(); + /*virtual*/ int isPlaying(); + /*virtual*/ void setGain(F32 vol); + /*virtual*/ F32 getGain(); + /*virtual*/ std::string getURL(); + +private: + LLPluginClassMedia* initializeMedia(const std::string& media_type); + + LLPluginClassMedia *mMediaPlugin; + + std::string mURL; + F32 mGain; +}; + + +#endif //LL_VIEWERMEDIA_STREAMINGAUDIO_H diff --git a/indra/newview/llviewermediafocus.cpp b/indra/newview/llviewermediafocus.cpp new file mode 100644 index 0000000000..60eabd730f --- /dev/null +++ b/indra/newview/llviewermediafocus.cpp @@ -0,0 +1,355 @@ +/** + * @file llviewermediafocus.cpp + * @brief Governs focus on Media prims + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "llviewerprecompiledheaders.h" + +#include "llviewermediafocus.h" + +//LLViewerMediaFocus +#include "llviewerobjectlist.h" +#include "llpanelmediahud.h" +#include "llpluginclassmedia.h" +#include "llagent.h" +#include "lltoolpie.h" +#include "llviewercamera.h" +#include "llviewermedia.h" +#include "llhudview.h" +#include "lluictrlfactory.h" +#include "lldrawable.h" +#include "llparcel.h" +#include "llviewerparcelmgr.h" +#include "llweb.h" +// +// LLViewerMediaFocus +// + +LLViewerMediaFocus::LLViewerMediaFocus() +: mMouseOverFlag(false) +{ +} + +LLViewerMediaFocus::~LLViewerMediaFocus() +{ + // The destructor for LLSingletons happens at atexit() time, which is too late to do much. + // Clean up in cleanupClass() instead. +} + +void LLViewerMediaFocus::cleanupClass() +{ + LLViewerMediaFocus *self = LLViewerMediaFocus::getInstance(); + + if(self) + { + // mMediaHUD will have been deleted by this point -- don't try to delete it. + + /* Richard says: + all widgets are supposed to be destroyed at the same time + you shouldn't hold on to pointer to them outside of ui code + you can use the LLHandle approach + if you want to be type safe, you'll need to add a LLRootHandle to whatever derived class you are pointing to + look at llview::gethandle + its our version of a weak pointer + */ + if(self->mMediaHUD.get()) + { + self->mMediaHUD.get()->setMediaImpl(NULL); + } + self->mMediaImpl = NULL; + } + +} + + +void LLViewerMediaFocus::setFocusFace( BOOL b, LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl ) +{ + LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); + if (b && media_impl.notNull()) + { + mMediaImpl = media_impl; + LLSelectMgr::getInstance()->deselectAll(); + LLSelectMgr::getInstance()->selectObjectOnly(objectp, face); + + mFocus = LLSelectMgr::getInstance()->getSelection(); + if(mMediaHUD.get() && ! parcel->getMediaPreventCameraZoom()) + { + mMediaHUD.get()->resetZoomLevel(); + mMediaHUD.get()->nextZoomLevel(); + } + if (!mFocus->isEmpty()) + { + gFocusMgr.setKeyboardFocus(this); + } + mObjectID = objectp->getID(); + // LLViewerMedia::addObserver(this, mObjectID); + + + } + else + { + gFocusMgr.setKeyboardFocus(NULL); + mFocus = NULL; + if(! parcel->getMediaPreventCameraZoom()) + { + gAgent.setFocusOnAvatar(TRUE, ANIMATE); + } + // LLViewerMedia::remObserver(this, mObjectID); + + // Null out the media hud media pointer + if(mMediaHUD.get()) + { + mMediaHUD.get()->setMediaImpl(NULL); + } + + // and null out the media impl + mMediaImpl = NULL; + } + if(mMediaHUD.get()) + { + mMediaHUD.get()->setMediaFocus(b); + } +} +bool LLViewerMediaFocus::getFocus() +{ + if (gFocusMgr.getKeyboardFocus() == this) + { + return true; + } + return false; +} + +// This function selects an ideal viewing distance given a selection bounding box, normal, and padding value +void LLViewerMediaFocus::setCameraZoom(F32 padding_factor) +{ + LLPickInfo& pick = LLToolPie::getInstance()->getPick(); + + if(LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + pick = mPickInfo; + setFocusFace(true, pick.getObject(), pick.mObjectFace, mMediaImpl); + } + + if (!LLSelectMgr::getInstance()->getSelection()->isEmpty()) + { + gAgent.setFocusOnAvatar(FALSE, ANIMATE); + + LLBBox selection_bbox = LLSelectMgr::getInstance()->getBBoxOfSelection(); + F32 height; + F32 width; + F32 depth; + F32 angle_of_view; + F32 distance; + + // We need the aspect ratio, and the 3 components of the bbox as height, width, and depth. + F32 aspect_ratio = getBBoxAspectRatio(selection_bbox, pick.mNormal, &height, &width, &depth); + F32 camera_aspect = LLViewerCamera::getInstance()->getAspect(); + + // We will normally use the side of the volume aligned with the short side of the screen (i.e. the height for + // a screen in a landscape aspect ratio), however there is an edge case where the aspect ratio of the object is + // more extreme than the screen. In this case we invert the logic, using the longer component of both the object + // and the screen. + bool invert = (camera_aspect > 1.0f && aspect_ratio > camera_aspect) || + (camera_aspect < 1.0f && aspect_ratio < camera_aspect); + + // To calculate the optimum viewing distance we will need the angle of the shorter side of the view rectangle. + // In portrait mode this is the width, and in landscape it is the height. + // We then calculate the distance based on the corresponding side of the object bbox (width for portrait, height for landscape) + // We will add half the depth of the bounding box, as the distance projection uses the center point of the bbox. + if(camera_aspect < 1.0f || invert) + { + angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect()); + distance = width * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); + } + else + { + angle_of_view = llmax(0.1f, LLViewerCamera::getInstance()->getView()); + distance = height * 0.5 * padding_factor / tan(angle_of_view * 0.5f ); + } + + distance += depth * 0.5; + + // Finally animate the camera to this new position and focal point + gAgent.setCameraPosAndFocusGlobal(LLSelectMgr::getInstance()->getSelectionCenterGlobal() + LLVector3d(pick.mNormal * distance), + LLSelectMgr::getInstance()->getSelectionCenterGlobal(), LLSelectMgr::getInstance()->getSelection()->getFirstObject()->mID ); + } +} +void LLViewerMediaFocus::onFocusReceived() +{ + if(mMediaImpl.notNull()) + mMediaImpl->focus(true); + + LLFocusableElement::onFocusReceived(); +} + +void LLViewerMediaFocus::onFocusLost() +{ + if(mMediaImpl.notNull()) + mMediaImpl->focus(false); + gViewerWindow->focusClient(); + mFocus = NULL; + LLFocusableElement::onFocusLost(); +} +void LLViewerMediaFocus::setMouseOverFlag(bool b, viewer_media_t media_impl) +{ + if (b && media_impl.notNull()) + { + if(! mMediaHUD.get()) + { + LLPanelMediaHUD* media_hud = new LLPanelMediaHUD(mMediaImpl); + mMediaHUD = media_hud->getHandle(); + gHUDView->addChild(media_hud); + } + mMediaHUD.get()->setMediaImpl(media_impl); + mMediaImpl = media_impl; + } + mMouseOverFlag = b; +} +LLUUID LLViewerMediaFocus::getSelectedUUID() +{ + LLViewerObject* object = mFocus->getFirstObject(); + return object ? object->getID() : LLUUID::null; +} +#if 0 // Must re-implement when the new media api event system is ready +void LLViewerMediaFocus::onNavigateComplete( const EventType& event_in ) +{ + if (hasFocus() && mLastURL != event_in.getStringValue()) + { + LLViewerMedia::focus(true, mObjectID); + // spoof mouse event to reassert focus + LLViewerMedia::mouseDown(1,1, mObjectID); + LLViewerMedia::mouseUp(1,1, mObjectID); + } + mLastURL = event_in.getStringValue(); +} +#endif +BOOL LLViewerMediaFocus::handleKey(KEY key, MASK mask, BOOL called_from_parent) +{ + if(mMediaImpl.notNull()) + mMediaImpl->handleKeyHere(key, mask); + return true; +} + +BOOL LLViewerMediaFocus::handleUnicodeChar(llwchar uni_char, BOOL called_from_parent) +{ + if(mMediaImpl.notNull()) + mMediaImpl->handleUnicodeCharHere(uni_char); + return true; +} +BOOL LLViewerMediaFocus::handleScrollWheel(S32 x, S32 y, S32 clicks) +{ + BOOL retval = FALSE; + if(mFocus.notNull() && mMediaImpl.notNull() && mMediaImpl->hasMedia()) + { + mMediaImpl->getMediaPlugin()->scrollEvent(x, y, clicks); + retval = TRUE; + } + return retval; +} + +void LLViewerMediaFocus::update() +{ + if (mMediaHUD.get()) + { + if(mFocus.notNull() || mMouseOverFlag || mMediaHUD.get()->isMouseOver()) + { + // mMediaHUD.get()->setVisible(true); + mMediaHUD.get()->updateShape(); + } + else + { + mMediaHUD.get()->setVisible(false); + } + } +} +// This function calculates the aspect ratio and the world aligned components of a selection bounding box. +F32 LLViewerMediaFocus::getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth) +{ + // Convert the selection normal and an up vector to local coordinate space of the bbox + LLVector3 local_normal = bbox.agentToLocalBasis(normal); + LLVector3 z_vec = bbox.agentToLocalBasis(LLVector3(0.0f, 0.0f, 1.0f)); + + LLVector3 comp1(0.f,0.f,0.f); + LLVector3 comp2(0.f,0.f,0.f); + LLVector3 bbox_max = bbox.getExtentLocal(); + F32 dot1 = 0.f; + F32 dot2 = 0.f; + + // The largest component of the localized normal vector is the depth component + // meaning that the other two are the legs of the rectangle. + local_normal.abs(); + if(local_normal.mV[VX] > local_normal.mV[VY]) + { + if(local_normal.mV[VX] > local_normal.mV[VZ]) + { + // Use the y and z comps + comp1.mV[VY] = bbox_max.mV[VY]; + comp2.mV[VZ] = bbox_max.mV[VZ]; + *depth = bbox_max.mV[VX]; + } + else + { + // Use the x and y comps + comp1.mV[VY] = bbox_max.mV[VY]; + comp2.mV[VZ] = bbox_max.mV[VZ]; + *depth = bbox_max.mV[VZ]; + } + } + else if(local_normal.mV[VY] > local_normal.mV[VZ]) + { + // Use the x and z comps + comp1.mV[VX] = bbox_max.mV[VX]; + comp2.mV[VZ] = bbox_max.mV[VZ]; + *depth = bbox_max.mV[VY]; + } + else + { + // Use the x and y comps + comp1.mV[VY] = bbox_max.mV[VY]; + comp2.mV[VZ] = bbox_max.mV[VZ]; + *depth = bbox_max.mV[VX]; + } + + // The height is the vector closest to vertical in the bbox coordinate space (highest dot product value) + dot1 = comp1 * z_vec; + dot2 = comp2 * z_vec; + if(fabs(dot1) > fabs(dot2)) + { + *height = comp1.length(); + *width = comp2.length(); + } + else + { + *height = comp2.length(); + *width = comp1.length(); + } + + // Return the aspect ratio. + return *width / *height; +} diff --git a/indra/newview/llviewermediafocus.h b/indra/newview/llviewermediafocus.h new file mode 100644 index 0000000000..a078d24b6a --- /dev/null +++ b/indra/newview/llviewermediafocus.h @@ -0,0 +1,90 @@ +/** + * @file llpanelmsgs.h + * @brief Message popup preferences panel + * + * $LicenseInfo:firstyear=2003&license=viewergpl$ + * + * Copyright (c) 2003-2007, 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_VIEWERMEDIAFOCUS_H +#define LL_VIEWERMEDIAFOCUS_H + +// includes for LLViewerMediaFocus +#include "llfocusmgr.h" +#include "llviewermedia.h" +#include "llviewerobject.h" +#include "llviewerwindow.h" +#include "llselectmgr.h" + +class LLViewerMediaImpl; +class LLPanelMediaHUD; + +class LLViewerMediaFocus : + public LLFocusableElement, + public LLSingleton<LLViewerMediaFocus> +{ +public: + LLViewerMediaFocus(); + ~LLViewerMediaFocus(); + + static void cleanupClass(); + + void setFocusFace(BOOL b, LLPointer<LLViewerObject> objectp, S32 face, viewer_media_t media_impl); + void clearFocus() { setFocusFace(false, NULL, 0, NULL); } + /*virtual*/ bool getFocus(); + /*virtual*/ // void onNavigateComplete( const EventType& event_in ); + + /*virtual*/ BOOL handleKey(KEY key, MASK mask, BOOL called_from_parent); + /*virtual*/ BOOL handleUnicodeChar(llwchar uni_char, BOOL called_from_parent); + BOOL handleScrollWheel(S32 x, S32 y, S32 clicks); + + LLUUID getSelectedUUID(); + LLObjectSelectionHandle getSelection() { return mFocus; } + + void update(); + + void setCameraZoom(F32 padding_factor); + void setMouseOverFlag(bool b, viewer_media_t media_impl = NULL); + bool getMouseOverFlag() { return mMouseOverFlag; } + void setPickInfo(LLPickInfo pick_info) { mPickInfo = pick_info; } + F32 getBBoxAspectRatio(const LLBBox& bbox, const LLVector3& normal, F32* height, F32* width, F32* depth); + +protected: + /*virtual*/ void onFocusReceived(); + /*virtual*/ void onFocusLost(); + +private: + LLObjectSelectionHandle mFocus; + std::string mLastURL; + bool mMouseOverFlag; + LLPickInfo mPickInfo; + LLHandle<LLPanelMediaHUD> mMediaHUD; + LLUUID mObjectID; + viewer_media_t mMediaImpl; +}; + + +#endif // LL_VIEWERMEDIAFOCUS_H diff --git a/indra/newview/llviewermediaobserver.h b/indra/newview/llviewermediaobserver.h new file mode 100644 index 0000000000..6667f982b6 --- /dev/null +++ b/indra/newview/llviewermediaobserver.h @@ -0,0 +1,71 @@ +/** + * @file llviewermediaobserver.h + * @brief Methods to override to catch events from LLViewerMedia class + * + * $LicenseInfo:firstyear=2007&license=viewergpl$ + * + * Copyright (c) 2007-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 LLVIEWERMEDIAOBSERVER_H +#define LLVIEWERMEDIAOBSERVER_H + +#include "llpluginclassmediaowner.h" + +class LLViewerMediaEventEmitter; + +class LLViewerMediaObserver : public LLPluginClassMediaOwner +{ +public: + virtual ~LLViewerMediaObserver(); + +private: + // Emitters will manage this list in addObserver/remObserver. + friend class LLViewerMediaEventEmitter; + std::list<LLViewerMediaEventEmitter *> mEmitters; +}; + + +#if 0 + // Classes that inherit from LLViewerMediaObserver should add this to their class declaration: + + // inherited from LLViewerMediaObserver + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); + + /* and will probably need to add this to their cpp file: + + #include "llpluginclassmedia.h" + + */ + + // The list of events is in llpluginclassmediaowner.h + + +#endif + + +#endif // LLVIEWERMEDIAOBSERVER_H + diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 67dcf6bcae..43e9a27484 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -40,7 +40,7 @@ #include <sstream> // linden library includes -#include "audioengine.h" +#include "llaudioengine.h" #include "llfloaterreg.h" #include "indra_constants.h" #include "llassetstorage.h" @@ -104,9 +104,8 @@ #include "llfloatergodtools.h" #include "llfloatergroupinvite.h" #include "llfloatergroups.h" -#include "llfloaterhtml.h" #include "llfloaterhtmlcurrency.h" -#include "llfloaterhtmlhelp.h" // gViewerHtmlHelp +#include "llfloatermediabrowser.h" // gViewerHtmlHelp #include "llfloaterhtmlsimple.h" #include "llfloaterhud.h" #include "llfloaterinspect.h" @@ -150,6 +149,7 @@ #include "llpanellogin.h" #include "llmenucommands.h" #include "llmenugl.h" +#include "llmimetypes.h" #include "llmorphview.h" #include "llmoveview.h" #include "llmutelist.h" @@ -461,13 +461,13 @@ void set_underclothes_menu_options() { if (gMenuHolder && gAgent.isTeen()) { - gMenuHolder->getChild<LLView>("Self Underpants", TRUE)->setVisible(FALSE); - gMenuHolder->getChild<LLView>("Self Undershirt", TRUE)->setVisible(FALSE); + gMenuHolder->getChild<LLView>("Self Underpants")->setVisible(FALSE); + gMenuHolder->getChild<LLView>("Self Undershirt")->setVisible(FALSE); } if (gMenuBarView && gAgent.isTeen()) { - gMenuBarView->getChild<LLView>("Menu Underpants", TRUE)->setVisible(FALSE); - gMenuBarView->getChild<LLView>("Menu Undershirt", TRUE)->setVisible(FALSE); + gMenuBarView->getChild<LLView>("Menu Underpants")->setVisible(FALSE); + gMenuBarView->getChild<LLView>("Menu Undershirt")->setVisible(FALSE); } } @@ -570,8 +570,8 @@ void init_menus() gAFKMenu = gMenuBarView->getChild<LLMenuItemCallGL>("Set Away", TRUE); gBusyMenu = gMenuBarView->getChild<LLMenuItemCallGL>("Set Busy", TRUE); - gAttachSubMenu = gMenuBarView->getChildMenuByName("Attach Object", TRUE); - gDetachSubMenu = gMenuBarView->getChildMenuByName("Detach Object", TRUE); + gAttachSubMenu = gMenuBarView->findChildMenuByName("Attach Object", TRUE); + gDetachSubMenu = gMenuBarView->findChildMenuByName("Detach Object", TRUE); gMenuBarView->createJumpKeys(); @@ -3461,7 +3461,10 @@ class LLSelfFriends : public view_listener_t bool handleEvent(const LLSD& userdata) { // Open "Friends" tab of the "People" panel in side tray. - LLSideTray::getInstance()->showPanel("panel_people", "friends_panel"); + LLSD param; + param["people_panel_tab_name"] = "friends_panel"; + + LLSideTray::getInstance()->showPanel("panel_people", param); return true; } }; @@ -3471,7 +3474,9 @@ class LLSelfGroups : public view_listener_t bool handleEvent(const LLSD& userdata) { // Open "Groups" tab of the "People" panel in side tray. - LLSideTray::getInstance()->showPanel("panel_people", "groups_panel"); + LLSD param; + param["people_panel_tab_name"] = "groups_panel"; + LLSideTray::getInstance()->showPanel("panel_people", param); return true; } }; @@ -5579,6 +5584,7 @@ class LLShowFloater : public view_listener_t } else if (floater_name == "help f1") { + llinfos << "Spawning HTML help window" << llendl; gViewerHtmlHelp.show(); } else if (floater_name == "complaint reporter") @@ -6883,7 +6889,7 @@ void handle_grab_texture(void* data) // user know that the image is now in inventory. if(view) { - LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); + LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); view->getPanel()->setSelection(item_id, TAKE_FOCUS_NO); view->getPanel()->openSelected(); @@ -7082,13 +7088,7 @@ void handle_load_from_xml(void*) void handle_web_browser_test(void*) { - const bool open_links_externally = false; - const bool open_app_slurls = true; - LLFloaterHtml::getInstance()->show( - "http://secondlife.com/app/search/slurls.html", - "Web Browser Test", - open_links_externally, - open_app_slurls); + LLWeb::loadURL("http://secondlife.com/app/search/slurls.html"); } void handle_buy_currency_test(void*) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index 02e9528f7d..1cfeec5627 100644 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -65,7 +65,7 @@ #include "llstring.h" #include "lltransactiontypes.h" #include "lluuid.h" -#include "vorbisencode.h" +#include "llvorbisencode.h" // system libraries #include <boost/tokenizer.hpp> diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 5849a40726..4503228cf2 100644 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -37,7 +37,7 @@ #include <deque> -#include "audioengine.h" +#include "llaudioengine.h" #include "indra_constants.h" #include "lscript_byteformat.h" #include "mean_collision_data.h" @@ -101,6 +101,7 @@ #include "llnotify.h" #include "llpanelgrouplandmoney.h" #include "llselectmgr.h" +#include "llsidetray.h" #include "llstartup.h" #include "llsky.h" #include "llstatenums.h" @@ -136,6 +137,7 @@ #include "llviewerdisplay.h" #include "llkeythrottle.h" #include "llgroupactions.h" +#include "llagentui.h" #include <boost/tokenizer.hpp> #include <boost/algorithm/string/split.hpp> @@ -902,12 +904,17 @@ void open_offer(const std::vector<LLUUID>& items, const std::string& from_name) break; case LLAssetType::AT_LANDMARK: { - // *TODO: Embed a link to the Places panel so that user can edit the landmark right away. LLInventoryCategory* parent_folder = gInventory.getCategory(item->getParentUUID()); LLSD args; args["LANDMARK_NAME"] = item->getName(); args["FOLDER_NAME"] = std::string(parent_folder ? parent_folder->getName() : "unnkown"); LLNotifications::instance().add("LandmarkCreated", args); + + // Open new landmark for editing in Places panel. + LLSD key; + key["type"] = "landmark"; + key["id"] = item->getUUID(); + LLSideTray::getInstance()->showPanel("panel_places", key); } break; case LLAssetType::AT_TEXTURE: @@ -964,7 +971,7 @@ void open_offer(const std::vector<LLUUID>& items, const std::string& from_name) if (view->getPanel()) { - LLUICtrl* focus_ctrl = gFocusMgr.getKeyboardFocus(); + LLFocusableElement* focus_ctrl = gFocusMgr.getKeyboardFocus(); view->getPanel()->setSelection(item->getUUID(), TAKE_FOCUS_NO); gFocusMgr.setKeyboardFocus(focus_ctrl); } @@ -1079,7 +1086,7 @@ bool LLOfferInfo::inventory_offer_callback(const LLSD& notification, const LLSD& msg->addUUIDFast(_PREHASH_ID, mTransactionID); msg->addU32Fast(_PREHASH_Timestamp, NO_TIMESTAMP); // no timestamp necessary std::string name; - gAgent.buildFullname(name); + LLAgentUI::buildFullname(name); msg->addStringFast(_PREHASH_FromAgentName, name); msg->addStringFast(_PREHASH_Message, ""); msg->addU32Fast(_PREHASH_ParentEstateID, 0); @@ -1526,7 +1533,7 @@ void process_improved_im(LLMessageSystem *msg, void **user_data) // if there is not a panel for this conversation (i.e. it is a new IM conversation // initiated by the other party) then... std::string my_name; - gAgent.buildFullname(my_name); + LLAgentUI::buildFullname(my_name); std::string response = gSavedPerAccountSettings.getString("BusyModeResponse2"); pack_instant_message( gMessageSystem, @@ -2131,7 +2138,7 @@ void busy_message (LLMessageSystem* msg, LLUUID from_id) if (gAgent.getBusy()) { std::string my_name; - gAgent.buildFullname(my_name); + LLAgentUI::buildFullname(my_name); std::string response = gSavedPerAccountSettings.getString("BusyModeResponse2"); pack_instant_message( gMessageSystem, @@ -2451,6 +2458,7 @@ void process_chat_from_simulator(LLMessageSystem *msg, void **user_data) // adding temporarily so that communications window chat bar // works until the new chat window is ready + chat.mText = from_name + ": " + chat.mText; LLFloaterChat::addChat(chat, FALSE, FALSE); } else @@ -3301,11 +3309,12 @@ void process_terse_object_update_improved(LLMessageSystem *mesgsys, void **user_ gObjectList.processCompressedObjectUpdate(mesgsys, user_data, OUT_TERSE_IMPROVED); } +static LLFastTimer::DeclareTimer FTM_PROCESS_OBJECTS("Process Objects"); void process_kill_object(LLMessageSystem *mesgsys, void **user_data) { - LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS); + LLFastTimer t(FTM_PROCESS_OBJECTS); LLUUID id; U32 local_id; @@ -4733,7 +4742,7 @@ void process_script_question(LLMessageSystem *msg, void **user_data) // so we'll reuse the same namespace for both throttle types. std::string throttle_name = owner_name; std::string self_name; - gAgent.getName( self_name ); + LLAgentUI::buildName( self_name ); if( owner_name == self_name ) { throttle_name = taskid.getString(); @@ -5038,9 +5047,6 @@ void process_teleport_local(LLMessageSystem *msg,void**) gAgent.setTeleportState( LLAgent::TELEPORT_NONE ); } - // Let the interested parties know we've teleported. - LLViewerParcelMgr::getInstance()->onTeleportFinished(); - // Sim tells us whether the new position is off the ground if (teleport_flags & TELEPORT_FLAGS_IS_FLYING) { @@ -5061,6 +5067,11 @@ void process_teleport_local(LLMessageSystem *msg,void**) gAgent.updateCamera(); send_agent_update(TRUE, TRUE); + + // Let the interested parties know we've teleported. + // Vadim *HACK: Agent position seems to get reset (to render position?) + // on each frame, so we have to pass the new position manually. + LLViewerParcelMgr::getInstance()->onTeleportFinished(true, gAgent.getPosGlobalFromAgent(pos)); } void send_simple_im(const LLUUID& to_id, @@ -5069,7 +5080,7 @@ void send_simple_im(const LLUUID& to_id, const LLUUID& id) { std::string my_name; - gAgent.buildFullname(my_name); + LLAgentUI::buildFullname(my_name); send_improved_im(to_id, my_name, message, @@ -5090,7 +5101,7 @@ void send_group_notice(const LLUUID& group_id, // This will mean converting the item to a binary bucket, // and the subject/message into a single field. std::string my_name; - gAgent.buildFullname(my_name); + LLAgentUI::buildFullname(my_name); // Combine subject + message into a single string. std::ostringstream subject_and_message; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index ec11e0aee2..1594a68f36 100644 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -34,7 +34,7 @@ #include "llviewerobject.h" -#include "audioengine.h" +#include "llaudioengine.h" #include "imageids.h" #include "indra_constants.h" #include "llmath.h" @@ -115,11 +115,13 @@ S32 LLViewerObject::sAxisArrowLength(50); BOOL LLViewerObject::sPulseEnabled(FALSE); BOOL LLViewerObject::sUseSharedDrawables(FALSE); // TRUE +static LLFastTimer::DeclareTimer FTM_CREATE_OBJECT("Create Object"); + // static LLViewerObject *LLViewerObject::createObject(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) { LLViewerObject *res = NULL; - LLFastTimer t1(LLFastTimer::FTM_CREATE_OBJECT); + LLFastTimer t1(FTM_CREATE_OBJECT); switch (pcode) { diff --git a/indra/newview/llviewerobjectlist.cpp b/indra/newview/llviewerobjectlist.cpp index 8939faeb91..a8232e9a9d 100644 --- a/indra/newview/llviewerobjectlist.cpp +++ b/indra/newview/llviewerobjectlist.cpp @@ -258,13 +258,15 @@ void LLViewerObjectList::processUpdateCore(LLViewerObject* objectp, } } +static LLFastTimer::DeclareTimer FTM_PROCESS_OBJECTS("Process Objects"); + void LLViewerObjectList::processObjectUpdate(LLMessageSystem *mesgsys, void **user_data, const EObjectUpdateType update_type, bool cached, bool compressed) { LLMemType mt(LLMemType::MTYPE_OBJECT_PROCESS_UPDATE); - LLFastTimer t(LLFastTimer::FTM_PROCESS_OBJECTS); + LLFastTimer t(FTM_PROCESS_OBJECTS); LLVector3d camera_global = gAgent.getCameraPositionGlobal(); LLViewerObject *objectp; @@ -1304,12 +1306,13 @@ LLViewerObject *LLViewerObjectList::createObjectViewer(const LLPCode pcode, LLVi } +static LLFastTimer::DeclareTimer FTM_CREATE_OBJECT("Create Object"); LLViewerObject *LLViewerObjectList::createObject(const LLPCode pcode, LLViewerRegion *regionp, const LLUUID &uuid, const U32 local_id, const LLHost &sender) { LLMemType mt(LLMemType::MTYPE_OBJECT); - LLFastTimer t(LLFastTimer::FTM_CREATE_OBJECT); + LLFastTimer t(FTM_CREATE_OBJECT); LLUUID fullid; if (uuid == LLUUID::null) diff --git a/indra/newview/llviewerparcelmedia.cpp b/indra/newview/llviewerparcelmedia.cpp index 26e21133ac..cb233085e5 100644 --- a/indra/newview/llviewerparcelmedia.cpp +++ b/indra/newview/llviewerparcelmedia.cpp @@ -41,47 +41,22 @@ #include "llviewerparcelmgr.h" #include "lluuid.h" #include "message.h" +#include "llviewermediafocus.h" #include "llviewerparcelmediaautoplay.h" #include "llviewerwindow.h" #include "llfirstuse.h" +#include "llpluginclassmedia.h" // Static Variables S32 LLViewerParcelMedia::sMediaParcelLocalID = 0; LLUUID LLViewerParcelMedia::sMediaRegionID; +viewer_media_t LLViewerParcelMedia::sMediaImpl; + // Local functions bool callback_play_media(const LLSD& notification, const LLSD& response, LLParcel* parcel); -// Move this to its own file. -// helper class that tries to download a URL from a web site and calls a method -// on the Panel Land Media and to discover the MIME type -class LLMimeDiscoveryResponder : public LLHTTPClient::Responder -{ -public: - LLMimeDiscoveryResponder( ) - {} - - - - virtual void completedHeader(U32 status, const std::string& reason, const LLSD& content) - { - std::string media_type = content["content-type"].asString(); - std::string::size_type idx1 = media_type.find_first_of(";"); - std::string mime_type = media_type.substr(0, idx1); - completeAny(status, mime_type); - } - - virtual void error( U32 status, const std::string& reason ) - { - completeAny(status, "none/none"); - } - - void completeAny(U32 status, const std::string& mime_type) - { - LLViewerMedia::setMimeType(mime_type); - } -}; // static void LLViewerParcelMedia::initClass() @@ -92,6 +67,13 @@ void LLViewerParcelMedia::initClass() LLViewerParcelMediaAutoPlay::initClass(); } +//static +void LLViewerParcelMedia::cleanupClass() +{ + // This needs to be destroyed before global destructor time. + sMediaImpl = NULL; +} + ////////////////////////////////////////////////////////////////////////////////////////// // static void LLViewerParcelMedia::update(LLParcel* parcel) @@ -105,6 +87,7 @@ void LLViewerParcelMedia::update(LLParcel* parcel) { sMediaRegionID = LLUUID() ; stop() ; + LL_DEBUGS("Media") << "no agent region, bailing out." << LL_ENDL; return ; } @@ -115,64 +98,54 @@ void LLViewerParcelMedia::update(LLParcel* parcel) LLUUID regionid = gAgent.getRegion()->getRegionID(); if (parcelid != sMediaParcelLocalID || regionid != sMediaRegionID) { + LL_DEBUGS("Media") << "New parcel, parcel id = " << parcelid << ", region id = " << regionid << LL_ENDL; sMediaParcelLocalID = parcelid; sMediaRegionID = regionid; new_parcel = true; } std::string mediaUrl = std::string ( parcel->getMediaURL () ); + std::string mediaCurrentUrl = std::string( parcel->getMediaCurrentURL()); + + // First use warning + if( ! mediaUrl.empty() && gWarningSettings.getBOOL("FirstStreamingVideo") ) + { + LLNotifications::instance().add("ParcelCanPlayMedia", LLSD(), LLSD(), + boost::bind(callback_play_media, _1, _2, parcel)); + return; + + } + + // if we have a current (link sharing) url, use it instead + if (mediaCurrentUrl != "" && parcel->getMediaType() == "text/html") + { + mediaUrl = mediaCurrentUrl; + } + LLStringUtil::trim(mediaUrl); + + // If no parcel media is playing, nothing left to do + if(sMediaImpl.isNull()) - // has something changed? - if ( ( LLViewerMedia::getMediaURL() != mediaUrl ) - || ( LLViewerMedia::getMediaTextureID() != parcel->getMediaID () ) ) { - bool video_was_playing = FALSE; - bool same_media_id = LLViewerMedia::getMediaTextureID() == parcel->getMediaID (); + return; + } - if (LLViewerMedia::isMediaPlaying()) + // Media is playing...has something changed? + else if (( sMediaImpl->getMediaURL() != mediaUrl ) + || ( sMediaImpl->getMediaTextureID() != parcel->getMediaID() ) + || ( sMediaImpl->getMimeType() != parcel->getMediaType() )) + { + // Only play if the media types are the same. + if(sMediaImpl->getMimeType() == parcel->getMediaType()) { - video_was_playing = TRUE; + play(parcel); } - if ( !mediaUrl.empty() && same_media_id && ! new_parcel) - { - // Someone has "changed the channel", changing the URL of a video - // you were already watching. Automatically play provided the texture ID is the same - if (video_was_playing) - { - // Poke the mime type in before calling play. - // This is necessary because in this instance we are not waiting - // for the results of a header curl. In order to change the channel - // a mime type MUST be provided. - LLViewerMedia::setMimeType(parcel->getMediaType()); - play(parcel); - } - } else { stop(); } - - // Discover the MIME type - // Disabled for the time being. Get the mime type from the parcel. - if(gSavedSettings.getBOOL("AutoMimeDiscovery")) - { - LLHTTPClient::getHeaderOnly( mediaUrl, new LLMimeDiscoveryResponder()); - } - else - { - LLViewerMedia::setMimeType(parcel->getMediaType()); - } - - // First use warning - if( gWarningSettings.getBOOL("FirstStreamingVideo") ) - { - LLNotifications::instance().add("ParcelCanPlayMedia", LLSD(), LLSD(), - boost::bind(callback_play_media, _1, _2, parcel)); - - } - } } else @@ -183,7 +156,7 @@ void LLViewerParcelMedia::update(LLParcel* parcel) /* else { - // no audio player, do a first use dialog if their is media here + // no audio player, do a first use dialog if there is media here if (parcel) { std::string mediaUrl = std::string ( parcel->getMediaURL () ); @@ -212,15 +185,53 @@ void LLViewerParcelMedia::play(LLParcel* parcel) return; std::string media_url = parcel->getMediaURL(); + std::string media_current_url = parcel->getMediaCurrentURL(); std::string mime_type = parcel->getMediaType(); LLUUID placeholder_texture_id = parcel->getMediaID(); U8 media_auto_scale = parcel->getMediaAutoScale(); U8 media_loop = parcel->getMediaLoop(); S32 media_width = parcel->getMediaWidth(); S32 media_height = parcel->getMediaHeight(); - LLViewerMedia::play(media_url, mime_type, placeholder_texture_id, - media_width, media_height, media_auto_scale, - media_loop); + + if(sMediaImpl) + { + // If the url and mime type are the same, call play again + if(sMediaImpl->getMediaURL() == media_url + && sMediaImpl->getMimeType() == mime_type + && sMediaImpl->getMediaTextureID() == placeholder_texture_id) + { + LL_DEBUGS("Media") << "playing with existing url " << media_url << LL_ENDL; + + sMediaImpl->play(); + } + // Else if the texture id's are the same, navigate and rediscover type + // MBW -- This causes other state from the previous parcel (texture size, autoscale, and looping) to get re-used incorrectly. + // It's also not really necessary -- just creating a new instance is fine. +// else if(sMediaImpl->getMediaTextureID() == placeholder_texture_id) +// { +// sMediaImpl->navigateTo(media_url, mime_type, true); +// } + else + { + // Since the texture id is different, we need to generate a new impl + LL_DEBUGS("Media") << "new media impl with mime type " << mime_type << ", url " << media_url << LL_ENDL; + + // Delete the old one first so they don't fight over the texture. + sMediaImpl->stop(); + + sMediaImpl = LLViewerMedia::newMediaImpl(media_url, placeholder_texture_id, + media_width, media_height, media_auto_scale, + media_loop); + } + } + else + { + // There is no media impl, make a new one + sMediaImpl = LLViewerMedia::newMediaImpl(media_url, placeholder_texture_id, + media_width, media_height, media_auto_scale, + media_loop); + } + LLFirstUse::useMedia(); LLViewerParcelMediaAutoPlay::playStarted(); @@ -229,20 +240,38 @@ void LLViewerParcelMedia::play(LLParcel* parcel) // static void LLViewerParcelMedia::stop() { + if(sMediaImpl.isNull()) + { + return; + } + + // We need to remove the media HUD if it is up. + LLViewerMediaFocus::getInstance()->clearFocus(); - LLViewerMedia::stop(); + // This will kill the media instance. + sMediaImpl->stop(); + sMediaImpl = NULL; } // static void LLViewerParcelMedia::pause() { - LLViewerMedia::pause(); + if(sMediaImpl.isNull()) + { + return; + } + sMediaImpl->pause(); } // static void LLViewerParcelMedia::start() { - LLViewerMedia::start(); + if(sMediaImpl.isNull()) + { + return; + } + sMediaImpl->start(); + LLFirstUse::useMedia(); LLViewerParcelMediaAutoPlay::playStarted(); @@ -251,16 +280,41 @@ void LLViewerParcelMedia::start() // static void LLViewerParcelMedia::seek(F32 time) { - LLViewerMedia::seek(time); + if(sMediaImpl.isNull()) + { + return; + } + sMediaImpl->seek(time); } - // static -LLMediaBase::EStatus LLViewerParcelMedia::getStatus() +void LLViewerParcelMedia::focus(bool focus) { - return LLViewerMedia::getStatus(); + sMediaImpl->focus(focus); } +// static +LLPluginClassMediaOwner::EMediaStatus LLViewerParcelMedia::getStatus() +{ + LLPluginClassMediaOwner::EMediaStatus result = LLPluginClassMediaOwner::MEDIA_NONE; + + if(sMediaImpl.notNull() && sMediaImpl->hasMedia()) + { + result = sMediaImpl->getMediaPlugin()->getStatus(); + } + + return result; +} + +// static +std::string LLViewerParcelMedia::getMimeType() +{ + return sMediaImpl.notNull() ? sMediaImpl->getMimeType() : "none/none"; +} +viewer_media_t LLViewerParcelMedia::getParcelMedia() +{ + return sMediaImpl; +} ////////////////////////////////////////////////////////////////////////////////////////// // static void LLViewerParcelMedia::processParcelMediaCommandMessage( LLMessageSystem *msg, void ** ) @@ -298,7 +352,7 @@ void LLViewerParcelMedia::processParcelMediaCommandMessage( LLMessageSystem *msg if(( command == PARCEL_MEDIA_COMMAND_PLAY ) || ( command == PARCEL_MEDIA_COMMAND_LOOP )) { - if (LLViewerMedia::isMediaPaused()) + if (getStatus() == LLViewerMediaImpl::MEDIA_PAUSED) { start(); } @@ -318,7 +372,7 @@ void LLViewerParcelMedia::processParcelMediaCommandMessage( LLMessageSystem *msg if (flags & (1<<PARCEL_MEDIA_COMMAND_TIME)) { - if(! LLViewerMedia::hasMedia()) + if(sMediaImpl.isNull()) { LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); play(parcel); @@ -382,6 +436,107 @@ void LLViewerParcelMedia::processParcelMediaUpdate( LLMessageSystem *msg, void * } } } +// Static +///////////////////////////////////////////////////////////////////////////////////////// +void LLViewerParcelMedia::sendMediaNavigateMessage(const std::string& url) +{ + std::string region_url = gAgent.getRegion()->getCapability("ParcelNavigateMedia"); + if (!region_url.empty()) + { + // send navigate event to sim for link sharing + LLSD body; + body["agent-id"] = gAgent.getID(); + body["local-id"] = LLViewerParcelMgr::getInstance()->getAgentParcel()->getLocalID(); + body["url"] = url; + LLHTTPClient::post(region_url, body, new LLHTTPClient::Responder); + } + else + { + llwarns << "can't get ParcelNavigateMedia capability" << llendl; + } + +} + +///////////////////////////////////////////////////////////////////////////////////////// +// inherited from LLViewerMediaObserver +// virtual +void LLViewerParcelMedia::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + switch(event) + { + case MEDIA_EVENT_CONTENT_UPDATED: + { + // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << LL_ENDL; + }; + break; + + case MEDIA_EVENT_TIME_DURATION_UPDATED: + { + // LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_SIZE_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_SIZE_CHANGED " << LL_ENDL; + }; + break; + + case MEDIA_EVENT_CURSOR_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_NAVIGATE_BEGIN: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN " << LL_ENDL; + }; + break; + + case MEDIA_EVENT_NAVIGATE_COMPLETE: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_PROGRESS_UPDATED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << LL_ENDL; + }; + break; + + case MEDIA_EVENT_STATUS_TEXT_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_LOCATION_CHANGED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_CLICK_LINK_HREF: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << LL_ENDL; + }; + break; + + case MEDIA_EVENT_PLUGIN_FAILED: + { + LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << LL_ENDL; + }; + break; + }; +} bool callback_play_media(const LLSD& notification, const LLSD& response, LLParcel* parcel) { @@ -401,3 +556,19 @@ bool callback_play_media(const LLSD& notification, const LLSD& response, LLParce return false; } +// TODO: observer +/* +void LLViewerParcelMediaNavigationObserver::onNavigateComplete( const EventType& event_in ) +{ + std::string url = event_in.getStringValue(); + + if (mCurrentURL != url && ! mFromMessage) + { + LLViewerParcelMedia::sendMediaNavigateMessage(url); + } + + mCurrentURL = url; + mFromMessage = false; + +} +*/ diff --git a/indra/newview/llviewerparcelmedia.h b/indra/newview/llviewerparcelmedia.h index 18988704d8..3f7f898356 100644 --- a/indra/newview/llviewerparcelmedia.h +++ b/indra/newview/llviewerparcelmedia.h @@ -33,18 +33,22 @@ #ifndef LLVIEWERPARCELMEDIA_H #define LLVIEWERPARCELMEDIA_H -#include "llmediabase.h" +#include "llviewermedia.h" class LLMessageSystem; class LLParcel; +class LLViewerParcelMediaNavigationObserver; + // This class understands land parcels, network traffic, LSL media // transport commands, and talks to the LLViewerMedia class to actually // do playback. It allows us to remove code from LLViewerParcelMgr. -class LLViewerParcelMedia +class LLViewerParcelMedia : public LLViewerMediaObserver { + LOG_CLASS(LLViewerParcelMedia); public: static void initClass(); + static void cleanupClass(); static void update(LLParcel* parcel); // called when the agent's parcel has a new URL, or the agent has @@ -60,17 +64,38 @@ class LLViewerParcelMedia static void start(); // restart after pause - no need for all the setup + static void focus(bool focus); + static void seek(F32 time); // jump to timecode time - static LLMediaBase::EStatus getStatus(); + static LLPluginClassMediaOwner::EMediaStatus getStatus(); + static std::string getMimeType(); + static viewer_media_t getParcelMedia(); static void processParcelMediaCommandMessage( LLMessageSystem *msg, void ** ); static void processParcelMediaUpdate( LLMessageSystem *msg, void ** ); + static void sendMediaNavigateMessage(const std::string& url); + + // inherited from LLViewerMediaObserver + virtual void handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event); public: static S32 sMediaParcelLocalID; static LLUUID sMediaRegionID; + // HACK: this will change with Media on a Prim + static viewer_media_t sMediaImpl; +}; + + +class LLViewerParcelMediaNavigationObserver +{ +public: + std::string mCurrentURL; + bool mFromMessage; + + // void onNavigateComplete( const EventType& event_in ); + }; #endif diff --git a/indra/newview/llviewerparcelmediaautoplay.cpp b/indra/newview/llviewerparcelmediaautoplay.cpp index 7ba2172c3f..1b79b47905 100644 --- a/indra/newview/llviewerparcelmediaautoplay.cpp +++ b/indra/newview/llviewerparcelmediaautoplay.cpp @@ -109,7 +109,7 @@ BOOL LLViewerParcelMediaAutoPlay::tick() if ((!mPlayed) && // if we've never played (mTimeInParcel > AUTOPLAY_TIME) && // and if we've been here for so many seconds (this_media_url.size() != 0) && // and if the parcel has media - (!LLViewerMedia::isMediaPlaying())) // and if the media is not already playing + (LLViewerParcelMedia::sMediaImpl.isNull())) // and if the media is not already playing { if (this_media_texture_id.notNull()) // and if the media texture is good { diff --git a/indra/newview/llviewerparcelmediaautoplay.h b/indra/newview/llviewerparcelmediaautoplay.h index cc2e70b1b9..16279e7f1f 100644 --- a/indra/newview/llviewerparcelmediaautoplay.h +++ b/indra/newview/llviewerparcelmediaautoplay.h @@ -33,7 +33,6 @@ #ifndef LLVIEWERPARCELMEDIAAUTOPLAY_H #define LLVIEWERPARCELMEDIAAUTOPLAY_H -#include "llmediabase.h" #include "lltimer.h" // timer to automatically play media diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp index b7f6cacc0e..d900ea9b33 100644 --- a/indra/newview/llviewerparcelmgr.cpp +++ b/indra/newview/llviewerparcelmgr.cpp @@ -35,7 +35,7 @@ #include "llviewerparcelmgr.h" // Library includes -#include "audioengine.h" +#include "llaudioengine.h" #include "indra_constants.h" #include "llcachename.h" #include "llgl.h" @@ -1531,7 +1531,7 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use if (instance->mTeleportInProgress) { instance->mTeleportInProgress = FALSE; - instance->mTeleportFinishedSignal(); + instance->mTeleportFinishedSignal(gAgent.getPositionGlobal()); } } } @@ -1604,6 +1604,9 @@ void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **use // Request access list information for this land LLViewerParcelMgr::getInstance()->sendParcelAccessListRequest(AL_ACCESS | AL_BAN); + // Request the media url filter list for this land + LLViewerParcelMgr::getInstance()->requestParcelMediaURLFilter(); + // Request dwell for this land, if it's not public land. LLViewerParcelMgr::getInstance()->mSelectedDwell = 0.f; if (0 != local_id) @@ -1927,6 +1930,66 @@ void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) } } +class LLParcelMediaURLFilterResponder : public LLHTTPClient::Responder +{ + virtual void result(const LLSD& content) + { + LLViewerParcelMgr::getInstance()->receiveParcelMediaURLFilter(content); + } +}; + +void LLViewerParcelMgr::requestParcelMediaURLFilter() +{ + if (!mSelected) + { + return; + } + + LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth ); + if (!region) + { + return; + } + + LLParcel* parcel = mCurrentParcel; + if (!parcel) + { + llwarns << "no parcel" << llendl; + return; + } + + LLSD body; + body["local-id"] = parcel->getLocalID(); + body["list"] = parcel->getMediaURLFilterList(); + + std::string url = region->getCapability("ParcelMediaURLFilterList"); + if (!url.empty()) + { + LLHTTPClient::post(url, body, new LLParcelMediaURLFilterResponder); + } + else + { + llwarns << "can't get ParcelMediaURLFilterList cap" << llendl; + } +} + + +void LLViewerParcelMgr::receiveParcelMediaURLFilter(const LLSD &content) +{ + if (content.has("list")) + { + LLParcel* parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel; + if (!parcel) return; + + if (content["local-id"].asInteger() == parcel->getLocalID()) + { + parcel->setMediaURLFilterList(content["list"]); + + LLViewerParcelMgr::getInstance()->notifyObservers(); + } + } +} + void LLViewerParcelMgr::deedLandToGroup() { @@ -2399,12 +2462,19 @@ LLViewerTexture* LLViewerParcelMgr::getPassImage() const return sPassImage; } -boost::signals2::connection LLViewerParcelMgr::setAgentParcelChangedCallback(parcel_changed_callback_t cb) +boost::signals2::connection LLViewerParcelMgr::addAgentParcelChangedCallback(parcel_changed_callback_t cb) { return mAgentParcelChangedSignal.connect(cb); } - -boost::signals2::connection LLViewerParcelMgr::setTeleportFinishedCallback(parcel_changed_callback_t cb) +/* + * Set finish teleport callback. You can use it to observe all teleport events. + * NOTE: + * After local( in one region) teleports we + * cannot rely on gAgent.getPositionGlobal(), + * so the new position gets passed explicitly. + * Use args of this callback to get global position of avatar after teleport event. + */ +boost::signals2::connection LLViewerParcelMgr::setTeleportFinishedCallback(teleport_finished_callback_t cb) { return mTeleportFinishedSignal.connect(cb); } @@ -2417,12 +2487,22 @@ boost::signals2::connection LLViewerParcelMgr::setTeleportFailedCallback(parcel_ /* Ok, we're notified that teleport has been finished. * We should now propagate the notification via mTeleportFinishedSignal * to all interested parties. - * However the agent parcel data has not been updated yet. - * Let's wait for the update and then emit the signal. */ -void LLViewerParcelMgr::onTeleportFinished() +void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos) { - mTeleportInProgress = TRUE; + if (local) + { + // Local teleport. We already have the agent parcel data. + // Emit the signal immediately. + getInstance()->mTeleportFinishedSignal(new_pos); + } + else + { + // Non-local teleport. + // The agent parcel data has not been updated yet. + // Let's wait for the update and then emit the signal. + mTeleportInProgress = TRUE; + } } void LLViewerParcelMgr::onTeleportFailed() diff --git a/indra/newview/llviewerparcelmgr.h b/indra/newview/llviewerparcelmgr.h index 427ed4a6f2..7373366cd7 100644 --- a/indra/newview/llviewerparcelmgr.h +++ b/indra/newview/llviewerparcelmgr.h @@ -82,6 +82,8 @@ class LLViewerParcelMgr : public LLSingleton<LLViewerParcelMgr> { public: + typedef boost::function<void (const LLVector3d&)> teleport_finished_callback_t; + typedef boost::signals2::signal<void (const LLVector3d&)> teleport_finished_signal_t; typedef boost::function<void()> parcel_changed_callback_t; typedef boost::signals2::signal<void()> parcel_changed_signal_t; @@ -204,6 +206,11 @@ public: // Takes an Access List flag, like AL_ACCESS or AL_BAN void sendParcelAccessListRequest(U32 flags); + // asks for the parcel's media url filter list + void requestParcelMediaURLFilter(); + // receive the response + void receiveParcelMediaURLFilter(const LLSD &content); + // Dwell is not part of the usual parcel update information because the // simulator doesn't actually know the per-parcel dwell. Ack! We have // to get it out of the database. @@ -262,10 +269,10 @@ public: // the agent is banned or not in the allowed group BOOL isCollisionBanned(); - boost::signals2::connection setAgentParcelChangedCallback(parcel_changed_callback_t cb); - boost::signals2::connection setTeleportFinishedCallback(parcel_changed_callback_t cb); + boost::signals2::connection addAgentParcelChangedCallback(parcel_changed_callback_t cb); + boost::signals2::connection setTeleportFinishedCallback(teleport_finished_callback_t cb); boost::signals2::connection setTeleportFailedCallback(parcel_changed_callback_t cb); - void onTeleportFinished(); + void onTeleportFinished(bool local, const LLVector3d& new_pos); void onTeleportFailed(); static BOOL isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power); @@ -316,7 +323,7 @@ private: LLDynamicArray<LLParcelObserver*> mObservers; BOOL mTeleportInProgress; - parcel_changed_signal_t mTeleportFinishedSignal; + teleport_finished_signal_t mTeleportFinishedSignal; parcel_changed_signal_t mTeleportFailedSignal; parcel_changed_signal_t mAgentParcelChangedSignal; diff --git a/indra/newview/llviewerpartsim.cpp b/indra/newview/llviewerpartsim.cpp index ec39819bc8..cfb8340462 100644 --- a/indra/newview/llviewerpartsim.cpp +++ b/indra/newview/llviewerpartsim.cpp @@ -631,6 +631,8 @@ void LLViewerPartSim::shift(const LLVector3 &offset) } } +static LLFastTimer::DeclareTimer FTM_SIMULATE_PARTICLES("Simulate Particles"); + void LLViewerPartSim::updateSimulation() { LLMemType mt(LLMemType::MTYPE_PARTICLES); @@ -644,7 +646,7 @@ void LLViewerPartSim::updateSimulation() return; } - LLFastTimer ftm(LLFastTimer::FTM_SIMULATE_PARTICLES); + LLFastTimer ftm(FTM_SIMULATE_PARTICLES); // Start at a random particle system so the same // particle system doesn't always get first pick at the diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp index fd3dc16745..2c68a106c3 100644 --- a/indra/newview/llviewerregion.cpp +++ b/indra/newview/llviewerregion.cpp @@ -1436,6 +1436,8 @@ void LLViewerRegion::setSeedCapability(const std::string& url) capabilityNames.append("MapLayerGod"); capabilityNames.append("NewFileAgentInventory"); capabilityNames.append("ParcelPropertiesUpdate"); + capabilityNames.append("ParcelMediaURLFilterList"); + capabilityNames.append("ParcelNavigateMedia"); capabilityNames.append("ParcelVoiceInfoRequest"); capabilityNames.append("ProductInfoRequest"); capabilityNames.append("ProvisionVoiceAccountRequest"); diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp index 709fcdcf01..994fbd8475 100644 --- a/indra/newview/llviewerstats.cpp +++ b/indra/newview/llviewerstats.cpp @@ -59,7 +59,6 @@ #include "llviewerregion.h" #include "llvoavatar.h" #include "llvoavatarself.h" -#include "llfloaterhtml.h" #include "llviewerwindow.h" // *TODO: remove, only used for width/height #include "llworld.h" #include "llfeaturemanager.h" @@ -594,14 +593,14 @@ void update_statistics(U32 frame_count) LLViewerStats::getInstance()->setStat(LLViewerStats::ST_SHADER_AVATAR, (F64)gSavedSettings.getBOOL("VertexShaderLevelAvatar")); LLViewerStats::getInstance()->setStat(LLViewerStats::ST_SHADER_ENVIRONMENT, (F64)gSavedSettings.getBOOL("VertexShaderLevelEnvironment")); #endif - LLViewerStats::getInstance()->setStat(LLViewerStats::ST_FRAME_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::NamedTimer::getRootNamedTimer().getFrameState())); - F64 idle_secs = gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_IDLE); - F64 network_secs = gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_NETWORK); + LLViewerStats::getInstance()->setStat(LLViewerStats::ST_FRAME_SECS, gDebugView->mFastTimerView->getTime("Frame")); + F64 idle_secs = gDebugView->mFastTimerView->getTime("Idle"); + F64 network_secs = gDebugView->mFastTimerView->getTime("Network"); LLViewerStats::getInstance()->setStat(LLViewerStats::ST_UPDATE_SECS, idle_secs - network_secs); LLViewerStats::getInstance()->setStat(LLViewerStats::ST_NETWORK_SECS, network_secs); - LLViewerStats::getInstance()->setStat(LLViewerStats::ST_IMAGE_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_IMAGE_UPDATE)); - LLViewerStats::getInstance()->setStat(LLViewerStats::ST_REBUILD_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_STATESORT )); - LLViewerStats::getInstance()->setStat(LLViewerStats::ST_RENDER_SECS, gDebugView->mFastTimerView->getTime(LLFastTimer::FTM_RENDER_GEOMETRY)); + LLViewerStats::getInstance()->setStat(LLViewerStats::ST_IMAGE_SECS, gDebugView->mFastTimerView->getTime("Update Images")); + LLViewerStats::getInstance()->setStat(LLViewerStats::ST_REBUILD_SECS, gDebugView->mFastTimerView->getTime("Sort Draw State")); + LLViewerStats::getInstance()->setStat(LLViewerStats::ST_RENDER_SECS, gDebugView->mFastTimerView->getTime("Geometry")); LLCircuitData *cdp = gMessageSystem->mCircuitInfo.findCircuit(gAgent.getRegion()->getHost()); if (cdp) diff --git a/indra/newview/llviewertexteditor.cpp b/indra/newview/llviewertexteditor.cpp index 9e0713b494..7bbe2c89b4 100644 --- a/indra/newview/llviewertexteditor.cpp +++ b/indra/newview/llviewertexteditor.cpp @@ -34,7 +34,7 @@ #include "llfloaterreg.h" #include "llfocusmgr.h" -#include "audioengine.h" +#include "llaudioengine.h" #include "llagent.h" #include "llinventory.h" #include "llinventorybridge.h" @@ -62,6 +62,7 @@ #include "llnotecard.h" #include "llmemorystream.h" #include "llmenugl.h" +#include "llscrollcontainer.h" #include "llavataractions.h" #include "llappviewer.h" // for gPacificDaylightTime @@ -108,6 +109,105 @@ public: } }; +// +// class LLEmbeddedItemSegment +// + +const S32 EMBEDDED_ITEM_LABEL_PADDING = 2; + +class LLEmbeddedItemSegment : public LLTextSegment +{ +public: + LLEmbeddedItemSegment(S32 pos, LLUIImagePtr image, LLPointer<LLInventoryItem> inv_item, LLTextEditor& editor) + : LLTextSegment(pos, pos + 1), + mImage(image), + mLabel(utf8str_to_wstring(inv_item->getName())), + mItem(inv_item), + mEditor(editor), + mHasMouseHover(false) + { + + mStyle = new LLStyle(LLStyle::Params().font(LLFontGL::getFontSansSerif())); + mToolTip = inv_item->getName() + '\n' + inv_item->getDescription(); + } + + /*virtual*/ S32 getWidth(S32 first_char, S32 num_chars) const + { + if (num_chars == 0) + { + return 0; + } + else + { + return EMBEDDED_ITEM_LABEL_PADDING + mImage->getWidth() + mStyle->getFont()->getWidth(mLabel.c_str()); + } + + } + //virtual S32 getOffset(S32 segment_local_x_coord, S32 start_offset, S32 num_chars, bool round) const; + //virtual void updateLayout(const class LLTextEditor& editor); + + /*virtual*/ S32 getNumChars(S32 num_pixels, S32 segment_offset, S32 line_offset, S32 max_chars) const + { + return 1; + } + /*virtual*/ F32 draw(S32 start, S32 end, S32 selection_start, S32 selection_end, const LLRect& draw_rect) + { + LLRect image_rect = draw_rect; + image_rect.mRight = image_rect.mLeft + mImage->getWidth(); + image_rect.mTop = image_rect.mBottom + mImage->getHeight(); + mImage->draw(image_rect); + + LLColor4 color; + if (mEditor.getReadOnly()) + { + color = LLUIColorTable::instance().getColor("TextEmbeddedItemReadOnlyColor"); + } + else + { + color = LLUIColorTable::instance().getColor("TextEmbeddedItemColor"); + } + + F32 right_x; + mStyle->getFont()->render(mLabel, 0, image_rect.mRight + EMBEDDED_ITEM_LABEL_PADDING, draw_rect.mBottom, color, LLFontGL::LEFT, LLFontGL::BOTTOM, mHasMouseHover ? LLFontGL::UNDERLINE : 0, LLFontGL::NO_SHADOW, mLabel.length(), S32_MAX, &right_x); + return right_x; + } + + /*virtual*/ S32 getMaxHeight() const + { + return llmax(mImage->getHeight(), llceil(mStyle->getFont()->getLineHeight())); + } + /*virtual*/ bool canEdit() const { return false; } + //virtual void unlinkFromDocument(class LLTextEditor* editor); + //virtual void linkToDocument(class LLTextEditor* editor); + + virtual void setHasMouseHover(bool hover) + { + mHasMouseHover = hover; + } + //virtual const LLColor4& getColor() const; + //virtual void setColor(const LLColor4 &color); + //virtual void setStyle(const LLStyleSP &style); + virtual BOOL getToolTip( std::string& msg ) const + { + msg = mToolTip; + return TRUE; + } + + /*virtual*/ const LLStyleSP getStyle() const { return mStyle; } + +private: + LLUIImagePtr mImage; + LLWString mLabel; + LLStyleSP mStyle; + std::string mToolTip; + LLPointer<LLInventoryItem> mItem; + LLTextEditor& mEditor; + bool mHasMouseHover; + +}; + + + //////////////////////////////////////////////////////////// // LLEmbeddedItems // @@ -130,13 +230,11 @@ public: // return true if there are no embedded items. bool empty(); - void bindEmbeddedChars(const LLFontGL* font) const; - void unbindEmbeddedChars(const LLFontGL* font) const; - BOOL insertEmbeddedItem(LLInventoryItem* item, llwchar* value, bool is_new); BOOL removeEmbeddedItem( llwchar ext_char ); BOOL hasEmbeddedItem(llwchar ext_char); // returns TRUE if /this/ editor has an entry for this item + LLUIImagePtr getItemImage(llwchar ext_char) const; void getEmbeddedItemList( std::vector<LLPointer<LLInventoryItem> >& items ); void addItems(const std::vector<LLPointer<LLInventoryItem> >& items); @@ -351,27 +449,13 @@ BOOL LLEmbeddedItems::hasEmbeddedItem(llwchar ext_char) return FALSE; } -void LLEmbeddedItems::bindEmbeddedChars( const LLFontGL* font ) const -{ - if( sEntries.empty() ) - { - return; - } - for (std::set<llwchar>::const_iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1) +LLUIImagePtr LLEmbeddedItems::getItemImage(llwchar ext_char) const +{ + LLInventoryItem* item = getEmbeddedItem(ext_char); + if (item) { - llwchar wch = *iter1; - item_map_t::iterator iter2 = sEntries.find(wch); - if (iter2 == sEntries.end()) - { - continue; - } - LLInventoryItem* item = iter2->second.mItem; - if (!item) - { - continue; - } - const char* img_name; + const char* img_name = ""; switch( item->getType() ) { case LLAssetType::AT_TEXTURE: @@ -428,27 +512,14 @@ void LLEmbeddedItems::bindEmbeddedChars( const LLFontGL* font ) const case LLAssetType::AT_GESTURE: img_name = "inv_item_gesture.tga"; break; //TODO need img_name case LLAssetType::AT_FAVORITE: img_name = "inv_item_landmark.tga"; break; - default: llassert(0); continue; + default: llassert(0); } - LLUIImagePtr image = LLUI::getUIImage(img_name); - - font->addEmbeddedChar( wch, image->getImage(), item->getName() ); + return LLUI::getUIImage(img_name); } + return LLUIImagePtr(); } -void LLEmbeddedItems::unbindEmbeddedChars( const LLFontGL* font ) const -{ - if( sEntries.empty() ) - { - return; - } - - for (std::set<llwchar>::const_iterator iter1 = mEmbeddedUsedChars.begin(); iter1 != mEmbeddedUsedChars.end(); ++iter1) - { - font->removeEmbeddedChar(*iter1); - } -} void LLEmbeddedItems::addItems(const std::vector<LLPointer<LLInventoryItem> >& items) { @@ -613,30 +684,16 @@ BOOL LLViewerTextEditor::handleToolTip(S32 x, S32 y, std::string& msg, LLRect* s } const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) + if( cur_segment && cur_segment->getToolTip( msg ) ) { - BOOL has_tool_tip = FALSE; - if( cur_segment->getStyle()->getIsEmbeddedItem() ) - { - LLWString wtip; - has_tool_tip = getEmbeddedItemToolTipAtPos(cur_segment->getStart(), wtip); - msg = wstring_to_utf8str(wtip); - } - else - { - has_tool_tip = cur_segment->getToolTip( msg ); - } - if( has_tool_tip ) - { - // Just use a slop area around the cursor - // Convert rect local to screen coordinates - S32 SLOP = 8; - localPointToScreen( - x - SLOP, y - SLOP, - &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); - sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; - sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; - } + // Just use a slop area around the cursor + // Convert rect local to screen coordinates + S32 SLOP = 8; + localPointToScreen( + x - SLOP, y - SLOP, + &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) ); + sticky_rect_screen->mRight = sticky_rect_screen->mLeft + 2 * SLOP; + sticky_rect_screen->mTop = sticky_rect_screen->mBottom + 2 * SLOP; } return TRUE; } @@ -648,21 +705,8 @@ BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) // Let scrollbar have first dibs handled = LLView::childrenHandleMouseDown(x, y, mask) != NULL; - // enable I Agree checkbox if the user scrolled through entire text - BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()); - if (mOnScrollEndCallback && was_scrolled_to_bottom) + if( !handled) { - mOnScrollEndCallback(mOnScrollEndData); - } - - if( !handled && mTakesNonScrollClicks) - { - if (!(mask & MASK_SHIFT)) - { - deselect(); - } - - BOOL start_select = TRUE; if( allowsEmbeddedItems() ) { setCursorAtLocalPos( x, y, FALSE ); @@ -685,192 +729,55 @@ BOOL LLViewerTextEditor::handleMouseDown(S32 x, S32 y, MASK mask) localPointToScreen(x, y, &screen_x, &screen_y ); LLToolDragAndDrop::getInstance()->setDragStart( screen_x, screen_y ); - start_select = FALSE; - } - else - { - mDragItem = NULL; - } - } - - if( start_select ) - { - // If we're not scrolling (handled by child), then we're selecting - if (mask & MASK_SHIFT) - { - S32 old_cursor_pos = mCursorPos; - setCursorAtLocalPos( x, y, TRUE ); - - if (hasSelection()) - { - /* Mac-like behavior - extend selection towards the cursor - if (mCursorPos < mSelectionStart - && mCursorPos < mSelectionEnd) - { - // ...left of selection - mSelectionStart = llmax(mSelectionStart, mSelectionEnd); - mSelectionEnd = mCursorPos; - } - else if (mCursorPos > mSelectionStart - && mCursorPos > mSelectionEnd) - { - // ...right of selection - mSelectionStart = llmin(mSelectionStart, mSelectionEnd); - mSelectionEnd = mCursorPos; - } - else - { - mSelectionEnd = mCursorPos; - } - */ - // Windows behavior - mSelectionEnd = mCursorPos; - } - else + if (hasTabStop()) { - mSelectionStart = old_cursor_pos; - mSelectionEnd = mCursorPos; + setFocus( TRUE ); } - // assume we're starting a drag select - mIsSelecting = TRUE; + handled = TRUE; } else { - setCursorAtLocalPos( x, y, TRUE ); - startSelection(); + mDragItem = NULL; } - gFocusMgr.setMouseCapture( this ); } - handled = TRUE; - } - - if (hasTabStop()) - { - setFocus(TRUE); - handled = TRUE; + if (!handled) + { + handled = LLTextEditor::handleMouseDown(x, y, mask); + } } - // Delay cursor flashing - resetKeystrokeTimer(); - return handled; } BOOL LLViewerTextEditor::handleHover(S32 x, S32 y, MASK mask) { - static LLUICachedControl<S32> scrollbar_size ("UIScrollbarSize", 0); - BOOL handled = FALSE; + BOOL handled = LLTextEditor::handleHover(x, y, mask); - if (!mDragItem) + if(hasMouseCapture() && mDragItem) { - // leave hover segment active during drag and drop - mHoverSegment = NULL; - } - if(hasMouseCapture() ) - { - if( mIsSelecting ) - { - if (x != mLastSelectionX || y != mLastSelectionY) - { - mLastSelectionX = x; - mLastSelectionY = y; - } + S32 screen_x; + S32 screen_y; + localPointToScreen(x, y, &screen_x, &screen_y ); - if( y > getTextRect().mTop ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() - 1 ); - } - else - if( y < getTextRect().mBottom ) - { - mScrollbar->setDocPos( mScrollbar->getDocPos() + 1 ); - } + mScroller->autoScroll(x, y); - setCursorAtLocalPos( x, y, TRUE ); - mSelectionEnd = mCursorPos; - - updateScrollFromCursor(); - getWindow()->setCursor(UI_CURSOR_IBEAM); - } - else if( mDragItem ) + if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) { - S32 screen_x; - S32 screen_y; - localPointToScreen(x, y, &screen_x, &screen_y ); - if( LLToolDragAndDrop::getInstance()->isOverThreshold( screen_x, screen_y ) ) - { - LLToolDragAndDrop::getInstance()->beginDrag( - LLAssetType::lookupDragAndDropType( mDragItem->getType() ), - mDragItem->getUUID(), - LLToolDragAndDrop::SOURCE_NOTECARD, - mPreviewID, mObjectID); + LLToolDragAndDrop::getInstance()->beginDrag( + LLAssetType::lookupDragAndDropType( mDragItem->getType() ), + mDragItem->getUUID(), + LLToolDragAndDrop::SOURCE_NOTECARD, + mPreviewID, mObjectID); - return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); - } - getWindow()->setCursor(UI_CURSOR_HAND); + return LLToolDragAndDrop::getInstance()->handleHover( x, y, mask ); } - - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" << llendl; + getWindow()->setCursor(UI_CURSOR_HAND); handled = TRUE; } - if( !handled ) - { - // Pass to children - handled = LLView::childrenHandleHover(x, y, mask) != NULL; - } - - if( handled ) - { - // Delay cursor flashing - resetKeystrokeTimer(); - } - - // Opaque - if( !handled && mTakesNonScrollClicks) - { - // Check to see if we're over an HTML-style link - if( !mSegments.empty() ) - { - const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment ) - { - if(cur_segment->getStyle()->isLink()) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over link, inactive)" << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - handled = TRUE; - } - else - if(cur_segment->getStyle()->getIsEmbeddedItem()) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (over embedded item, inactive)" << llendl; - getWindow()->setCursor(UI_CURSOR_HAND); - //getWindow()->setCursor(UI_CURSOR_ARROW); - handled = TRUE; - } - mHoverSegment = cur_segment; - } - } - - if( !handled ) - { - lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl; - if (!mScrollbar->getVisible() || x < getRect().getWidth() - scrollbar_size) - { - getWindow()->setCursor(UI_CURSOR_IBEAM); - } - else - { - getWindow()->setCursor(UI_CURSOR_ARROW); - } - handled = TRUE; - } - } - return handled; } @@ -903,13 +810,6 @@ BOOL LLViewerTextEditor::handleMouseUp(S32 x, S32 y, MASK mask) handled = LLTextEditor::handleMouseUp(x,y,mask); - // Used to enable I Agree checkbox if the user scrolled through entire text - BOOL was_scrolled_to_bottom = (mScrollbar->getDocPos() == mScrollbar->getDocPosMax()); - if (mOnScrollEndCallback && was_scrolled_to_bottom) - { - mOnScrollEndCallback(mOnScrollEndData); - } - return handled; } @@ -949,24 +849,6 @@ BOOL LLViewerTextEditor::handleRightMouseDown(S32 x, S32 y, MASK mask) return handled; } -BOOL LLViewerTextEditor::handleMiddleMouseDown(S32 x, S32 y, MASK mask) -{ - BOOL handled = FALSE; - handled = childrenHandleMiddleMouseDown(x, y, mask) != NULL; - if (!handled) - { - handled = LLTextEditor::handleMiddleMouseDown(x, y, mask); - } - return handled; -} - -BOOL LLViewerTextEditor::handleMiddleMouseUp(S32 x, S32 y, MASK mask) -{ - BOOL handled = childrenHandleMiddleMouseUp(x, y, mask) != NULL; - - return handled; -} - BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; @@ -974,14 +856,15 @@ BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) // let scrollbar have first dibs handled = LLView::childrenHandleDoubleClick(x, y, mask) != NULL; - if( !handled && mTakesNonScrollClicks) + if( !handled) { if( allowsEmbeddedItems() ) { - const LLTextSegment* cur_segment = getSegmentAtLocalPos( x, y ); - if( cur_segment && cur_segment->getStyle()->getIsEmbeddedItem() ) + S32 doc_index = getDocIndexFromLocalCoord(x, y, FALSE); + llwchar doc_char = getWText()[doc_index]; + if (mEmbeddedItemList->hasEmbeddedItem(doc_char)) { - if( openEmbeddedItemAtPos( cur_segment->getStart() ) ) + if( openEmbeddedItemAtPos( doc_index )) { deselect(); setFocus( FALSE ); @@ -989,47 +872,7 @@ BOOL LLViewerTextEditor::handleDoubleClick(S32 x, S32 y, MASK mask) } } } - - setCursorAtLocalPos( x, y, FALSE ); - deselect(); - - const LLWString &text = getWText(); - - if( isPartOfWord( text[mCursorPos] ) ) - { - // Select word the cursor is over - while ((mCursorPos > 0) && isPartOfWord(text[mCursorPos-1])) - { - mCursorPos--; - } - startSelection(); - - while ((mCursorPos < (S32)text.length()) && isPartOfWord( text[mCursorPos] ) ) - { - mCursorPos++; - } - - mSelectionEnd = mCursorPos; - } - else if ((mCursorPos < (S32)text.length()) && !iswspace( text[mCursorPos]) ) - { - // Select the character the cursor is over - startSelection(); - mCursorPos++; - mSelectionEnd = mCursorPos; - } - - // We don't want handleMouseUp() to "finish" the selection (and thereby - // set mSelectionEnd to where the mouse is), so we finish the selection here. - mIsSelecting = FALSE; - - // delay cursor flashing - resetKeystrokeTimer(); - - // take selection to 'primary' clipboard - updatePrimary(); - - handled = TRUE; + handled = LLTextEditor::handleDoubleClick(x, y, mask); } return handled; } @@ -1051,80 +894,78 @@ BOOL LLViewerTextEditor::handleDragAndDrop(S32 x, S32 y, MASK mask, return FALSE; } - if (mTakesNonScrollClicks) + if (getEnabled() && acceptsTextInput()) { - if (getEnabled() && acceptsTextInput()) + switch( cargo_type ) { - switch( cargo_type ) + case DAD_CALLINGCARD: + case DAD_TEXTURE: + case DAD_SOUND: + case DAD_LANDMARK: + case DAD_SCRIPT: + case DAD_CLOTHING: + case DAD_OBJECT: + case DAD_NOTECARD: + case DAD_BODYPART: + case DAD_ANIMATION: + case DAD_GESTURE: { - case DAD_CALLINGCARD: - case DAD_TEXTURE: - case DAD_SOUND: - case DAD_LANDMARK: - case DAD_SCRIPT: - case DAD_CLOTHING: - case DAD_OBJECT: - case DAD_NOTECARD: - case DAD_BODYPART: - case DAD_ANIMATION: - case DAD_GESTURE: + LLInventoryItem *item = (LLInventoryItem *)cargo_data; + if( item && allowsEmbeddedItems() ) { - LLInventoryItem *item = (LLInventoryItem *)cargo_data; - if( item && allowsEmbeddedItems() ) + U32 mask_next = item->getPermissions().getMaskNextOwner(); + if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) { - U32 mask_next = item->getPermissions().getMaskNextOwner(); - if((mask_next & PERM_ITEM_UNRESTRICTED) == PERM_ITEM_UNRESTRICTED) - { - if( drop ) - { - deselect(); - S32 old_cursor = mCursorPos; - setCursorAtLocalPos( x, y, TRUE ); - S32 insert_pos = mCursorPos; - setCursorPos(old_cursor); - BOOL inserted = insertEmbeddedItem( insert_pos, item ); - if( inserted && (old_cursor > mCursorPos) ) - { - setCursorPos(mCursorPos + 1); - } - - updateLineStartList(); - } - *accept = ACCEPT_YES_COPY_MULTI; - } - else + if( drop ) { - *accept = ACCEPT_NO; - if (tooltip_msg.empty()) + deselect(); + S32 old_cursor = mCursorPos; + setCursorAtLocalPos( x, y, TRUE ); + S32 insert_pos = mCursorPos; + setCursorPos(old_cursor); + BOOL inserted = insertEmbeddedItem( insert_pos, item ); + if( inserted && (old_cursor > mCursorPos) ) { - // *TODO: Translate - tooltip_msg.assign("Only items with unrestricted\n" - "'next owner' permissions \n" - "can be attached to notecards."); + setCursorPos(mCursorPos + 1); } + + needsReflow(); + } + *accept = ACCEPT_YES_COPY_MULTI; } else { *accept = ACCEPT_NO; + if (tooltip_msg.empty()) + { + // *TODO: Translate + tooltip_msg.assign("Only items with unrestricted\n" + "'next owner' permissions \n" + "can be attached to notecards."); + } } - break; } - - default: + else + { *accept = ACCEPT_NO; - break; + } + break; } - } - else - { - // Not enabled + + default: *accept = ACCEPT_NO; + break; } - - handled = TRUE; - lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLViewerTextEditor " << getName() << llendl; } + else + { + // Not enabled + *accept = ACCEPT_NO; + } + + handled = TRUE; + lldebugst(LLERR_USER_INPUT) << "dragAndDrop handled by LLViewerTextEditor " << getName() << llendl; return handled; } @@ -1244,33 +1085,33 @@ llwchar LLViewerTextEditor::pasteEmbeddedItem(llwchar ext_char) return LL_UNKNOWN_CHAR; // item not found or list full } -void LLViewerTextEditor::bindEmbeddedChars(const LLFontGL* font) const +void LLViewerTextEditor::onValueChange(S32 start, S32 end) { - mEmbeddedItemList->bindEmbeddedChars( font ); + updateSegments(); + findEmbeddedItemSegments(start, end); } -void LLViewerTextEditor::unbindEmbeddedChars(const LLFontGL* font) const +void LLViewerTextEditor::findEmbeddedItemSegments(S32 start, S32 end) { - mEmbeddedItemList->unbindEmbeddedChars( font ); -} + LLWString text = getWText(); -BOOL LLViewerTextEditor::getEmbeddedItemToolTipAtPos(S32 pos, LLWString &msg) const -{ - if (pos < getLength()) + LLColor4 text_color = ( mReadOnly ? mReadOnlyFgColor.get() : mFgColor.get() ); + + // Start with i just after the first embedded item + for(S32 idx = start; idx < end; idx++ ) { - LLInventoryItem* item = LLEmbeddedItems::getEmbeddedItem(getWChar(pos)); - if( item ) + llwchar embedded_char = text[idx]; + if( embedded_char >= FIRST_EMBEDDED_CHAR + && embedded_char <= LAST_EMBEDDED_CHAR + && mEmbeddedItemList->hasEmbeddedItem(embedded_char) ) { - msg = utf8str_to_wstring(item->getName()); - msg += '\n'; - msg += utf8str_to_wstring(item->getDescription()); - return TRUE; + LLInventoryItem* itemp = mEmbeddedItemList->getEmbeddedItem(embedded_char); + LLUIImagePtr image = mEmbeddedItemList->getItemImage(embedded_char); + insertSegment(new LLEmbeddedItemSegment(idx, image, itemp, *this)); } } - return FALSE; } - BOOL LLViewerTextEditor::openEmbeddedItemAtPos(S32 pos) { if( pos < getLength()) diff --git a/indra/newview/llviewertexteditor.h b/indra/newview/llviewertexteditor.h index 1a69c6869d..9567bfbc48 100644 --- a/indra/newview/llviewertexteditor.h +++ b/indra/newview/llviewertexteditor.h @@ -65,8 +65,6 @@ public: // mousehandler overrides virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); - virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); - virtual BOOL handleMiddleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleHover(S32 x, S32 y, MASK mask); virtual BOOL handleDoubleClick(S32 x, S32 y, MASK mask ); @@ -80,6 +78,8 @@ public: virtual BOOL importBuffer(const char* buffer, S32 length); virtual bool importStream(std::istream& str); virtual BOOL exportBuffer(std::string& buffer); + virtual void onValueChange(S32 start, S32 end); + void setNotecardInfo(const LLUUID& notecard_item_id, const LLUUID& object_id, const LLUUID& preview_id) { mNotecardInventoryID = notecard_item_id; @@ -106,11 +106,9 @@ public: private: // Embedded object operations + void findEmbeddedItemSegments(S32 start, S32 end); virtual llwchar pasteEmbeddedItem(llwchar ext_char); - virtual void bindEmbeddedChars(const LLFontGL* font) const; - virtual void unbindEmbeddedChars(const LLFontGL* font) const; - BOOL getEmbeddedItemToolTipAtPos(S32 pos, LLWString &wmsg) const; BOOL openEmbeddedItemAtPos( S32 pos ); BOOL openEmbeddedItem(LLInventoryItem* item, llwchar wc); @@ -128,6 +126,7 @@ private: static bool onNotecardDialog(const LLSD& notification, const LLSD& response ); LLPointer<LLInventoryItem> mDragItem; + LLTextSegment* mDragSegment; llwchar mDragItemChar; BOOL mDragItemSaved; class LLEmbeddedItems* mEmbeddedItemList; diff --git a/indra/newview/llviewertexturelist.cpp b/indra/newview/llviewertexturelist.cpp index ca9e89723c..dac2331ca3 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" @@ -51,7 +53,6 @@ #include "llxmltree.h" #include "message.h" -#include "llagent.h" #include "lltexturecache.h" #include "lltexturefetch.h" #include "llviewercontrol.h" @@ -61,8 +62,7 @@ #include "llviewerstats.h" #include "pipeline.h" #include "llappviewer.h" -#include "lluictrlfactory.h" // for LLXUIParser -#include <sys/stat.h> +#include "llxuiparser.h" //////////////////////////////////////////////////////////////////////////// @@ -74,6 +74,7 @@ const S32 IMAGES_MAX_PACKET_UPDATES = 1; // Only send N packets of IMAGES_PER_RE const F32 RESEND_IMAGE_REQUEST_TIME = 15.f; // seconds LLViewerTextureList gTextureList; +static LLFastTimer::DeclareTimer FTM_PROCESS_IMAGES("Process Images"); /////////////////////////////////////////////////////////////////////////////// @@ -541,6 +542,7 @@ void LLViewerTextureList::dirtyImage(LLViewerFetchedTexture *image) } //////////////////////////////////////////////////////////////////////////// +static LLFastTimer::DeclareTimer FTM_IMAGE_MARK_DIRTY("Dirty Images"); void LLViewerTextureList::updateImages(F32 max_time) { @@ -558,7 +560,7 @@ void LLViewerTextureList::updateImages(F32 max_time) max_time = llmin(llmax(max_time, 0.001f*10.f*gFrameIntervalSeconds), 0.001f); if (!mDirtyTextureList.empty()) { - LLFastTimer t(LLFastTimer::FTM_IMAGE_MARK_DIRTY); + LLFastTimer t(FTM_IMAGE_MARK_DIRTY); gPipeline.dirtyPoolObjectTextures(mDirtyTextureList); mDirtyTextureList.clear(); } @@ -581,7 +583,7 @@ void LLViewerTextureList::updateImages(F32 max_time) } if (!gNoRender && !gGLManager.mIsDisabled) { - LLViewerMedia::updateImagesMediaStreams(); + LLViewerMedia::updateMedia(); } updateImagesUpdateStats(); } @@ -696,6 +698,7 @@ void LLViewerTextureList::updateImagesDecodePriorities() return type_from_host; } */ +static LLFastTimer::DeclareTimer FTM_IMAGE_CREATE("Create Images"); F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) { @@ -705,7 +708,7 @@ F32 LLViewerTextureList::updateImagesCreateTextures(F32 max_time) // Create GL textures for all textures that need them (images which have been // decoded, but haven't been pushed into GL). // - LLFastTimer t(LLFastTimer::FTM_IMAGE_CREATE); + LLFastTimer t(FTM_IMAGE_CREATE); LLTimer create_timer; image_list_t::iterator enditer = mCreateTextureList.begin(); @@ -1114,7 +1117,7 @@ void LLViewerTextureList::updateMaxResidentTexMem(S32 mem) // static void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_data) { - LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES); + LLFastTimer t(FTM_PROCESS_IMAGES); // Receive image header, copy into image object and decompresses // if this is a one-packet image. @@ -1178,7 +1181,7 @@ void LLViewerTextureList::receiveImageHeader(LLMessageSystem *msg, void **user_d void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_data) { LLMemType mt1(LLMemType::MTYPE_APPFMTIMAGE); - LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES); + LLFastTimer t(FTM_PROCESS_IMAGES); // Receives image packet, copy into image object, // checks if all packets received, decompresses if so. @@ -1243,7 +1246,7 @@ void LLViewerTextureList::receiveImagePacket(LLMessageSystem *msg, void **user_d // static void LLViewerTextureList::processImageNotInDatabase(LLMessageSystem *msg,void **user_data) { - LLFastTimer t(LLFastTimer::FTM_PROCESS_IMAGES); + LLFastTimer t(FTM_PROCESS_IMAGES); LLUUID image_id; msg->getUUIDFast(_PREHASH_ImageID, _PREHASH_ID, image_id); diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 2aa8e4d314..8b7bb83d0d 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -60,7 +60,7 @@ // // linden library includes -#include "audioengine.h" // mute on minimize +#include "llaudioengine.h" // mute on minimize #include "indra_constants.h" #include "llassetstorage.h" #include "llerrorcontrol.h" @@ -169,6 +169,8 @@ #include "llviewertexturelist.h" #include "llviewerinventory.h" #include "llviewerkeyboard.h" +#include "llviewermedia.h" +#include "llviewermediafocus.h" #include "llviewermenu.h" #include "llviewermessage.h" #include "llviewerobjectlist.h" @@ -190,6 +192,7 @@ #include "llpostprocess.h" #include "llbottomtray.h" #include "llnearbychatbar.h" +#include "llagentui.h" #include "llnotificationmanager.h" @@ -940,8 +943,6 @@ void LLViewerWindow::handleFocus(LLWindow *window) gAgent.onAppFocusGained(); LLToolMgr::getInstance()->onAppFocusGained(); - gShowTextEditCursor = TRUE; - // See if we're coming in with modifier keys held down if (gKeyboard) { @@ -971,11 +972,6 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) showCursor(); getWindow()->setMouseClipping(FALSE); - // JC - Leave keyboard focus, so if you're popping in and out editing - // a script, you don't have to click in the editor again and again. - // gFocusMgr.setKeyboardFocus( NULL ); - gShowTextEditCursor = FALSE; - // If losing focus while keys are down, reset them. if (gKeyboard) { @@ -1117,7 +1113,7 @@ BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255))); std::string name_str; - gAgent.getName(name_str); + LLAgentUI::buildName(name_str); std::string temp_str; temp_str = llformat( "%s FPS %3.1f Phy FPS %2.1f Time Dil %1.3f", /* Flawfinder: ignore */ @@ -1166,7 +1162,7 @@ void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data) case SLURL_MESSAGE_TYPE: // received URL std::string url = (const char*)data; - LLWebBrowserCtrl* web = NULL; + LLMediaCtrl* web = NULL; const bool trusted_browser = false; if (LLURLDispatcher::dispatch(url, web, trusted_browser)) { @@ -1666,6 +1662,19 @@ void LLViewerWindow::initWorldUI() //Notification Manager LLNotificationsUI::LLNotificationManager* notify_manager = LLNotificationsUI::LLNotificationManager::getInstance(); getRootView()->addChild(notify_manager); + + if ( gHUDView == NULL ) + { + LLRect hud_rect = full_window; + hud_rect.mBottom += 50; + if (gMenuBarView) + { + hud_rect.mTop -= gMenuBarView->getRect().getHeight(); + } + gHUDView = new LLHUDView(hud_rect); + // put behind everything else in the UI + getRootView()->addChildInBack(gHUDView); + } } // Destroy the UI @@ -2026,6 +2035,11 @@ void LLViewerWindow::draw() // No translation needed, this view is glued to 0,0 mRootView->draw(); + if (mToolTip->getVisible() && LLView::sDebugRects) + { + gl_rect_2d(mToolTipStickyRect, LLColor4::white, false); + } + // Draw optional on-top-of-everyone view LLUICtrl* top_ctrl = gFocusMgr.getTopCtrl(); if (top_ctrl && top_ctrl->getVisible()) @@ -2102,7 +2116,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) if (key < 0x80) { // Not a special key, so likely (we hope) to generate a character. Let it fall through to character handler first. - return gFocusMgr.childHasKeyboardFocus(mRootView); + return (gFocusMgr.getKeyboardFocus() != NULL); } } @@ -2185,7 +2199,7 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) } // Traverses up the hierarchy - LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus(); + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); if( keyboard_focus ) { LLLineEditor* chat_editor = LLBottomTray::instanceExists() ? LLBottomTray::getInstance()->getNearbyChatBar()->getChatBox() : NULL; @@ -2320,7 +2334,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) } // Traverses up the hierarchy - LLView* keyboard_focus = gFocusMgr.getKeyboardFocus(); + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); if( keyboard_focus ) { if (keyboard_focus->handleUnicodeChar(uni_char, FALSE)) @@ -2428,6 +2442,7 @@ void LLViewerWindow::updateBottomTrayRect() rc.mRight = right; bottom_tray->reshape(rc.getWidth(), rc.getHeight(), FALSE); bottom_tray->setRect(rc); + mOnBottomTrayWidthChanged(); } } } @@ -2445,7 +2460,7 @@ void LLViewerWindow::updateUI() updateWorldViewRect(); - updateBottomTrayRect(); + //updateBottomTrayRect(); LLView::sMouseHandlerMessage.clear(); @@ -2809,6 +2824,12 @@ void LLViewerWindow::updatePicking(S32 x, S32 y, MASK mask) do_pick = FALSE; } + if(LLViewerMediaFocus::getInstance()->getFocus()) + { + // When in-world media is in focus, pick every frame so that browser mouse-overs, dragging scrollbars, etc. work properly. + do_pick = TRUE; + } + if (do_pick) { mouse_moved_since_pick = FALSE; @@ -2913,7 +2934,7 @@ void LLViewerWindow::updateMouseDelta() void LLViewerWindow::updateKeyboardFocus() { // clean up current focus - LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus(); + LLUICtrl* cur_focus = dynamic_cast<LLUICtrl*>(gFocusMgr.getKeyboardFocus()); if (cur_focus) { if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain()) @@ -3325,7 +3346,7 @@ void LLViewerWindow::schedulePick(LLPickInfo& pick_info) return; } - llassert_always(pick_info.mScreenRegion.notNull()); + llassert_always(pick_info.mScreenRegion.notEmpty()); mPicks.push_back(pick_info); // delay further event processing until we receive results of pick @@ -4283,7 +4304,6 @@ void LLViewerWindow::drawMouselookInstructions() LLFontGL::HCENTER, LLFontGL::TOP); } - S32 LLViewerWindow::getWindowHeight() const { return mVirtualWindowRect.getHeight(); @@ -4753,7 +4773,7 @@ BOOL LLViewerWindow::changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, BOOL result_first_try = FALSE; BOOL result_second_try = FALSE; - LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus(); + LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); send_agent_pause(); llinfos << "Stopping GL during changeDisplaySettings" << llendl; stopGL(); @@ -5128,12 +5148,8 @@ void LLPickInfo::updateXYCoords() LLPointer<LLViewerTexture> imagep = LLViewerTextureManager::getFetchedTexture(tep->getID()); if(mUVCoords.mV[VX] >= 0.f && mUVCoords.mV[VY] >= 0.f && imagep.notNull()) { - LLCoordGL coords; - - coords.mX = llround(mUVCoords.mV[VX] * (F32)imagep->getWidth()); - coords.mY = llround(mUVCoords.mV[VY] * (F32)imagep->getHeight()); - - gViewerWindow->getWindow()->convertCoords(coords, &mXYCoords); + mXYCoords.mX = llround(mUVCoords.mV[VX] * (F32)imagep->getWidth()); + mXYCoords.mY = llround((1.f - mUVCoords.mV[VY]) * (F32)imagep->getHeight()); } } } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 60b0d3c34b..8502f7a719 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -189,6 +189,12 @@ public: /*virtual*/ std::string translateString(const char* tag); /*virtual*/ std::string translateString(const char* tag, const std::map<std::string, std::string>& args); + + // signal on bottom tray width changed + typedef boost::function<void (void)> bottom_tray_callback_t; + typedef boost::signals2::signal<void (void)> bottom_tray_signal_t; + bottom_tray_signal_t mOnBottomTrayWidthChanged; + boost::signals2::connection setOnBottomTrayWidthChanged(bottom_tray_callback_t cb) { return mOnBottomTrayWidthChanged.connect(cb); } // // ACCESSORS diff --git a/indra/newview/llvlmanager.cpp b/indra/newview/llvlmanager.cpp index 177093c4d1..07ef262668 100644 --- a/indra/newview/llvlmanager.cpp +++ b/indra/newview/llvlmanager.cpp @@ -40,7 +40,6 @@ #include "patch_dct.h" #include "llviewerregion.h" #include "llframetimer.h" -#include "llagent.h" #include "llsurface.h" LLVLManager gVLManager; diff --git a/indra/newview/llvoavatar.cpp b/indra/newview/llvoavatar.cpp index 2b5c3361c4..98d8b27e09 100644 --- a/indra/newview/llvoavatar.cpp +++ b/indra/newview/llvoavatar.cpp @@ -37,7 +37,7 @@ #include <stdio.h> #include <ctype.h> -#include "audioengine.h" +#include "llaudioengine.h" #include "noise.h" #include "llagent.h" // Get state values from here @@ -2085,13 +2085,16 @@ S32 LLVOAvatar::setTETexture(const U8 te, const LLUUID& uuid) } } +static LLFastTimer::DeclareTimer FTM_AVATAR_UPDATE("Update Avatar"); +static LLFastTimer::DeclareTimer FTM_JOINT_UPDATE("Update Joints"); + //------------------------------------------------------------------------ // idleUpdate() //------------------------------------------------------------------------ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) { LLMemType mt(LLMemType::MTYPE_AVATAR); - LLFastTimer t(LLFastTimer::FTM_AVATAR_UPDATE); + LLFastTimer t(FTM_AVATAR_UPDATE); if (isDead()) { @@ -2110,7 +2113,7 @@ BOOL LLVOAvatar::idleUpdate(LLAgent &agent, LLWorld &world, const F64 &time) // force asynchronous drawable update if(mDrawable.notNull() && !gNoRender) { - LLFastTimer t(LLFastTimer::FTM_JOINT_UPDATE); + LLFastTimer t(FTM_JOINT_UPDATE); if (mIsSitting && getParent()) { @@ -2272,6 +2275,8 @@ void LLVOAvatar::idleUpdateVoiceVisualizer(bool voice_enabled) }//if ( voiceEnabled ) } +static LLFastTimer::DeclareTimer FTM_ATTACHMENT_UPDATE("Update Attachments"); + void LLVOAvatar::idleUpdateMisc(bool detailed_update) { if (LLVOAvatar::sJointDebug) @@ -2293,7 +2298,7 @@ void LLVOAvatar::idleUpdateMisc(bool detailed_update) // update attachments positions if (detailed_update || !sUseImpostors) { - LLFastTimer t(LLFastTimer::FTM_ATTACHMENT_UPDATE); + LLFastTimer t(FTM_ATTACHMENT_UPDATE); for (attachment_map_t::iterator iter = mAttachmentPoints.begin(); iter != mAttachmentPoints.end(); ) { @@ -4613,9 +4618,11 @@ void LLVOAvatar::requestStopMotion( LLMotion* motion ) //----------------------------------------------------------------------------- // loadAvatar() //----------------------------------------------------------------------------- +static LLFastTimer::DeclareTimer FTM_LOAD_AVATAR("Load Avatar"); + BOOL LLVOAvatar::loadAvatar() { -// LLFastTimer t(LLFastTimer::FTM_LOAD_AVATAR); +// LLFastTimer t(FTM_LOAD_AVATAR); // avatar_skeleton.xml if( !buildSkeleton(sAvatarSkeletonInfo) ) @@ -5168,9 +5175,10 @@ LLDrawable *LLVOAvatar::createDrawable(LLPipeline *pipeline) //----------------------------------------------------------------------------- // updateGeometry() //----------------------------------------------------------------------------- +static LLFastTimer::DeclareTimer FTM_UPDATE_AVATAR("Update Avatar"); BOOL LLVOAvatar::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_AVATAR); + LLFastTimer ftm(FTM_UPDATE_AVATAR); if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_AVATAR))) { return TRUE; diff --git a/indra/newview/llvoavatar.h b/indra/newview/llvoavatar.h index 59be38a1b0..4dc70511ce 100644 --- a/indra/newview/llvoavatar.h +++ b/indra/newview/llvoavatar.h @@ -192,6 +192,8 @@ public: public: virtual bool isSelf() const { return false; } // True if this avatar is for this viewer's agent + bool isBuilt() const { return mIsBuilt; } + private: BOOL mSupportsAlphaLayers; // For backwards compatibility, TRUE for 1.23+ clients diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 9df25bdb11..9777e1ec21 100644 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -38,7 +38,7 @@ #include <stdio.h> #include <ctype.h> -#include "audioengine.h" +#include "llaudioengine.h" #include "noise.h" // TODO: Seraph - Remove unnecessary headers. These are copied from llvoavatar.h. @@ -990,6 +990,19 @@ LLViewerJointAttachment *LLVOAvatarSelf::attachObject(LLViewerObject *viewer_obj return attachment; } +void LLVOAvatarSelf::getAllAttachmentsArray(LLDynamicArray<S32>& attachments) +{ + for (LLVOAvatar::attachment_map_t::const_iterator iter = mAttachmentPoints.begin(); + iter != mAttachmentPoints.end(); ++iter) + { + LLViewerJointAttachment* attachment = iter->second; + if ( attachment && (attachment->getNumObjects() > 0)) + { + attachments.push_back(iter->first); + } + } +} + U32 LLVOAvatarSelf::getNumWearables(LLVOAvatarDefines::ETextureIndex i) const { EWearableType type = LLVOAvatarDictionary::getInstance()->getTEWearableType(i); diff --git a/indra/newview/llvoavatarself.h b/indra/newview/llvoavatarself.h index e34f09aca2..17744cce1b 100644 --- a/indra/newview/llvoavatarself.h +++ b/indra/newview/llvoavatarself.h @@ -261,6 +261,7 @@ public: LLViewerObject* getWornAttachment(const LLUUID& inv_item_id ) const; const std::string getAttachedPointName(const LLUUID& inv_item_id) const; /*virtual*/ LLViewerJointAttachment *attachObject(LLViewerObject *viewer_object); + void getAllAttachmentsArray(LLDynamicArray<S32>& attachments); //-------------------------------------------------------------------- // HUDs diff --git a/indra/newview/llvoclouds.cpp b/indra/newview/llvoclouds.cpp index fbc4e2e609..130e1fd749 100644 --- a/indra/newview/llvoclouds.cpp +++ b/indra/newview/llvoclouds.cpp @@ -115,9 +115,11 @@ LLDrawable* LLVOClouds::createDrawable(LLPipeline *pipeline) return mDrawable; } +static LLFastTimer::DeclareTimer FTM_UPDATE_CLOUDS("Update Clouds"); + BOOL LLVOClouds::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_CLOUDS); + LLFastTimer ftm(FTM_UPDATE_CLOUDS); if (!(gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_CLOUDS))) { return TRUE; diff --git a/indra/newview/llvograss.cpp b/indra/newview/llvograss.cpp index cf6eb8e9fd..a2793accea 100644 --- a/indra/newview/llvograss.cpp +++ b/indra/newview/llvograss.cpp @@ -382,9 +382,11 @@ LLDrawable* LLVOGrass::createDrawable(LLPipeline *pipeline) return mDrawable; } +static LLFastTimer::DeclareTimer FTM_UPDATE_GRASS("Update Grass"); + BOOL LLVOGrass::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_GRASS); + LLFastTimer ftm(FTM_UPDATE_GRASS); dirtySpatialGroup(); plantBlades(); return TRUE; diff --git a/indra/newview/llvoground.cpp b/indra/newview/llvoground.cpp index fe19e18151..ac2484ddfd 100644 --- a/indra/newview/llvoground.cpp +++ b/indra/newview/llvoground.cpp @@ -37,7 +37,6 @@ #include "llviewercontrol.h" -#include "llagent.h" #include "lldrawable.h" #include "llface.h" #include "llsky.h" diff --git a/indra/newview/llvopartgroup.cpp b/indra/newview/llvopartgroup.cpp index 29036f4947..38c9ce28c4 100644 --- a/indra/newview/llvopartgroup.cpp +++ b/indra/newview/llvopartgroup.cpp @@ -139,9 +139,10 @@ LLVector3 LLVOPartGroup::getCameraPosition() const return gAgent.getCameraPositionAgent(); } +static LLFastTimer::DeclareTimer FTM_UPDATE_PARTICLES("Update Particles"); BOOL LLVOPartGroup::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_PARTICLES); + LLFastTimer ftm(FTM_UPDATE_PARTICLES); dirtySpatialGroup(); @@ -416,12 +417,15 @@ void LLParticlePartition::addGeometryCount(LLSpatialGroup* group, U32& vertex_co } } +static LLFastTimer::DeclareTimer FTM_REBUILD_GRASS_VB("Grass VB"); +static LLFastTimer::DeclareTimer FTM_REBUILD_PARTICLE_VB("Particle VB"); + void LLParticlePartition::getGeometry(LLSpatialGroup* group) { LLMemType mt(LLMemType::MTYPE_SPACE_PARTITION); LLFastTimer ftm(mDrawableType == LLPipeline::RENDER_TYPE_GRASS ? - LLFastTimer::FTM_REBUILD_GRASS_VB : - LLFastTimer::FTM_REBUILD_PARTICLE_VB); + FTM_REBUILD_GRASS_VB : + FTM_REBUILD_PARTICLE_VB); std::sort(mFaceList.begin(), mFaceList.end(), LLFace::CompareDistanceGreater()); diff --git a/indra/newview/llvosky.cpp b/indra/newview/llvosky.cpp index 993cf522e9..ae5992099d 100644 --- a/indra/newview/llvosky.cpp +++ b/indra/newview/llvosky.cpp @@ -1231,6 +1231,8 @@ void LLVOSky::createDummyVertexBuffer() } } +static LLFastTimer::DeclareTimer FTM_RENDER_FAKE_VBO_UPDATE("Fake VBO Update"); + void LLVOSky::updateDummyVertexBuffer() { if(!LLVertexBuffer::sEnableVBOs) @@ -1242,7 +1244,7 @@ void LLVOSky::updateDummyVertexBuffer() return ; } - LLFastTimer t(LLFastTimer::FTM_RENDER_FAKE_VBO_UPDATE) ; + LLFastTimer t(FTM_RENDER_FAKE_VBO_UPDATE) ; if(!mFace[FACE_DUMMY] || mFace[FACE_DUMMY]->mVertexBuffer.isNull()) createDummyVertexBuffer() ; @@ -1255,10 +1257,11 @@ void LLVOSky::updateDummyVertexBuffer() //---------------------------------- //end of fake vertex buffer updating //---------------------------------- +static LLFastTimer::DeclareTimer FTM_GEO_SKY("Sky Geometry"); BOOL LLVOSky::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_GEO_SKY); + LLFastTimer ftm(FTM_GEO_SKY); if (mFace[FACE_REFLECTION] == NULL) { LLDrawPoolWater *poolp = (LLDrawPoolWater*) gPipeline.getPool(LLDrawPool::POOL_WATER); diff --git a/indra/newview/llvosurfacepatch.cpp b/indra/newview/llvosurfacepatch.cpp index 4980b50de4..157d719fcc 100644 --- a/indra/newview/llvosurfacepatch.cpp +++ b/indra/newview/llvosurfacepatch.cpp @@ -176,10 +176,11 @@ LLDrawable *LLVOSurfacePatch::createDrawable(LLPipeline *pipeline) return mDrawable; } +static LLFastTimer::DeclareTimer FTM_UPDATE_TERRAIN("Update Terrain"); BOOL LLVOSurfacePatch::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_TERRAIN); + LLFastTimer ftm(FTM_UPDATE_TERRAIN); dirtySpatialGroup(); @@ -1028,9 +1029,10 @@ LLVertexBuffer* LLTerrainPartition::createVertexBuffer(U32 type_mask, U32 usage) return new LLVertexBufferTerrain(); } +static LLFastTimer::DeclareTimer FTM_REBUILD_TERRAIN_VB("Terrain VB"); void LLTerrainPartition::getGeometry(LLSpatialGroup* group) { - LLFastTimer ftm(LLFastTimer::FTM_REBUILD_TERRAIN_VB); + LLFastTimer ftm(FTM_REBUILD_TERRAIN_VB); LLVertexBuffer* buffer = group->mVertexBuffer; diff --git a/indra/newview/llvotextbubble.cpp b/indra/newview/llvotextbubble.cpp index 9871965458..f5094c025e 100644 --- a/indra/newview/llvotextbubble.cpp +++ b/indra/newview/llvotextbubble.cpp @@ -39,7 +39,6 @@ #include "llprimitive.h" #include "llrendersphere.h" -#include "llagent.h" #include "llbox.h" #include "lldrawable.h" #include "llface.h" diff --git a/indra/newview/llvotree.cpp b/indra/newview/llvotree.cpp index d1cac4c77e..86d9f31d07 100644 --- a/indra/newview/llvotree.cpp +++ b/indra/newview/llvotree.cpp @@ -504,9 +504,11 @@ LLDrawable* LLVOTree::createDrawable(LLPipeline *pipeline) const S32 LEAF_INDICES = 24; const S32 LEAF_VERTICES = 16; +static LLFastTimer::DeclareTimer FTM_UPDATE_TREE("Update Tree"); + BOOL LLVOTree::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_TREE); + LLFastTimer ftm(FTM_UPDATE_TREE); if (mReferenceBuffer.isNull() || mDrawable->getFace(0)->mVertexBuffer.isNull()) { diff --git a/indra/newview/llvovolume.cpp b/indra/newview/llvovolume.cpp index bef38bb669..940accdd06 100644 --- a/indra/newview/llvovolume.cpp +++ b/indra/newview/llvovolume.cpp @@ -47,7 +47,7 @@ #include "material_codes.h" #include "message.h" #include "object_flags.h" -#include "llagent.h" +#include "llagentconstants.h" #include "lldrawable.h" #include "lldrawpoolbump.h" #include "llface.h" @@ -79,6 +79,9 @@ F32 LLVOVolume::sLODSlopDistanceFactor = 0.5f; //Changing this to zero, effectiv F32 LLVOVolume::sDistanceFactor = 1.0f; S32 LLVOVolume::sNumLODChanges = 0; +static LLFastTimer::DeclareTimer FTM_GEN_TRIANGLES("Generate Triangles"); +static LLFastTimer::DeclareTimer FTM_GEN_VOLUME("Generate Volumes"); + LLVOVolume::LLVOVolume(const LLUUID &id, const LLPCode pcode, LLViewerRegion *regionp) : LLViewerObject(id, pcode, regionp), mVolumeImpl(NULL) @@ -1130,15 +1133,18 @@ void LLVOVolume::updateRelativeXform() } } +static LLFastTimer::DeclareTimer FTM_GEN_FLEX("Generate Flexies"); +static LLFastTimer::DeclareTimer FTM_UPDATE_PRIMITIVES("Update Primitives"); + BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) { - LLFastTimer t(LLFastTimer::FTM_UPDATE_PRIMITIVES); + LLFastTimer t(FTM_UPDATE_PRIMITIVES); if (mVolumeImpl != NULL) { BOOL res; { - LLFastTimer t(LLFastTimer::FTM_GEN_FLEX); + LLFastTimer t(FTM_GEN_FLEX); res = mVolumeImpl->doUpdateGeometry(drawable); } updateFaceFlags(); @@ -1162,14 +1168,14 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) if (mVolumeChanged) { - LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME); + LLFastTimer ftm(FTM_GEN_VOLUME); LLVolumeParams volume_params = getVolume()->getParams(); setVolume(volume_params, 0); drawable->setState(LLDrawable::REBUILD_VOLUME); } { - LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES); + LLFastTimer t(FTM_GEN_TRIANGLES); regenFaces(); genBBoxes(FALSE); } @@ -1186,7 +1192,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) old_volumep = NULL ; { - LLFastTimer ftm(LLFastTimer::FTM_GEN_VOLUME); + LLFastTimer ftm(FTM_GEN_VOLUME); LLVolumeParams volume_params = getVolume()->getParams(); setVolume(volume_params, 0); } @@ -1204,7 +1210,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) drawable->setState(LLDrawable::REBUILD_VOLUME); // for face->genVolumeTriangles() { - LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES); + LLFastTimer t(FTM_GEN_TRIANGLES); if (new_num_faces != old_num_faces) { regenFaces(); @@ -1218,7 +1224,7 @@ BOOL LLVOVolume::updateGeometry(LLDrawable *drawable) { compiled = TRUE; // All it did was move or we changed the texture coordinate offset - LLFastTimer t(LLFastTimer::FTM_GEN_TRIANGLES); + LLFastTimer t(FTM_GEN_TRIANGLES); genBBoxes(FALSE); } @@ -1753,7 +1759,7 @@ void LLVOVolume::generateSilhouette(LLSelectNode* nodep, const LLVector3& view_p trans_mat.translate(getRegion()->getOriginAgent()); } - volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, trans_mat, mRelativeXformInvTrans); + volume->generateSilhouetteVertices(nodep->mSilhouetteVertices, nodep->mSilhouetteNormals, nodep->mSilhouetteSegments, view_vector, trans_mat, mRelativeXformInvTrans, nodep->getTESelectMask()); nodep->mSilhouetteExists = TRUE; } @@ -2235,6 +2241,9 @@ void LLVolumeGeometryManager::getGeometry(LLSpatialGroup* group) } +static LLFastTimer::DeclareTimer FTM_REBUILD_VOLUME_VB("Volume"); +static LLFastTimer::DeclareTimer FTM_REBUILD_VBO("VBO Rebuilt"); + void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { if (LLPipeline::sSkipUpdate) @@ -2253,8 +2262,8 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) { if (group->isState(LLSpatialGroup::MESH_DIRTY) && !LLPipeline::sDelayVBUpdate) { - LLFastTimer ftm(LLFastTimer::FTM_REBUILD_VBO); - LLFastTimer ftm2(LLFastTimer::FTM_REBUILD_VOLUME_VB); + LLFastTimer ftm(FTM_REBUILD_VBO); + LLFastTimer ftm2(FTM_REBUILD_VOLUME_VB); rebuildMesh(group); } @@ -2262,9 +2271,9 @@ void LLVolumeGeometryManager::rebuildGeom(LLSpatialGroup* group) } group->mBuilt = 1.f; - LLFastTimer ftm(LLFastTimer::FTM_REBUILD_VBO); + LLFastTimer ftm(FTM_REBUILD_VBO); - LLFastTimer ftm2(LLFastTimer::FTM_REBUILD_VOLUME_VB); + LLFastTimer ftm2(FTM_REBUILD_VOLUME_VB); group->clearDrawMap(); diff --git a/indra/newview/llvowater.cpp b/indra/newview/llvowater.cpp index 427119285b..e2357957ab 100644 --- a/indra/newview/llvowater.cpp +++ b/indra/newview/llvowater.cpp @@ -37,7 +37,6 @@ #include "imageids.h" #include "llviewercontrol.h" -#include "llagent.h" #include "lldrawable.h" #include "lldrawpoolwater.h" #include "llface.h" @@ -139,9 +138,11 @@ LLDrawable *LLVOWater::createDrawable(LLPipeline *pipeline) return mDrawable; } +static LLFastTimer::DeclareTimer FTM_UPDATE_WATER("Update Water"); + BOOL LLVOWater::updateGeometry(LLDrawable *drawable) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_WATER); + LLFastTimer ftm(FTM_UPDATE_WATER); LLFace *face; if (drawable->getNumFaces() < 1) diff --git a/indra/newview/llvowlsky.cpp b/indra/newview/llvowlsky.cpp index abd25e6598..8621e5e1d9 100644 --- a/indra/newview/llvowlsky.cpp +++ b/indra/newview/llvowlsky.cpp @@ -307,9 +307,11 @@ void LLVOWLSky::restoreGL() gPipeline.markRebuild(mDrawable, LLDrawable::REBUILD_ALL, TRUE); } +static LLFastTimer::DeclareTimer FTM_GEO_SKY("Sky Geometry"); + BOOL LLVOWLSky::updateGeometry(LLDrawable * drawable) { - LLFastTimer ftm(LLFastTimer::FTM_GEO_SKY); + LLFastTimer ftm(FTM_GEO_SKY); LLStrider<LLVector3> vertices; LLStrider<LLVector2> texCoords; LLStrider<U16> indices; diff --git a/indra/newview/llwaterparammanager.cpp b/indra/newview/llwaterparammanager.cpp index 92c223e43d..3661be500b 100644 --- a/indra/newview/llwaterparammanager.cpp +++ b/indra/newview/llwaterparammanager.cpp @@ -263,9 +263,11 @@ void LLWaterParamManager::updateShaderUniforms(LLGLSLShader * shader) } } +static LLFastTimer::DeclareTimer FTM_UPDATE_WLPARAM("Update Windlight Params"); + void LLWaterParamManager::update(LLViewerCamera * cam) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_WLPARAM); + LLFastTimer ftm(FTM_UPDATE_WLPARAM); // update the shaders and the menu propagateParameters(); diff --git a/indra/newview/llweb.cpp b/indra/newview/llweb.cpp index 57f57f75d7..300a5db7c3 100644 --- a/indra/newview/llweb.cpp +++ b/indra/newview/llweb.cpp @@ -40,7 +40,7 @@ #include "llviewerwindow.h" #include "llviewercontrol.h" -#include "llfloaterhtmlhelp.h" +#include "llfloatermediabrowser.h" #include "llfloaterreg.h" #include "llalertdialog.h" diff --git a/indra/newview/llwind.cpp b/indra/newview/llwind.cpp index ae98bead98..cbee4ddae1 100644 --- a/indra/newview/llwind.cpp +++ b/indra/newview/llwind.cpp @@ -49,7 +49,6 @@ // viewer #include "noise.h" #include "v4color.h" -#include "llagent.h" #include "llworld.h" diff --git a/indra/newview/llwlparammanager.cpp b/indra/newview/llwlparammanager.cpp index c237c0bded..7d9d0a6191 100644 --- a/indra/newview/llwlparammanager.cpp +++ b/indra/newview/llwlparammanager.cpp @@ -52,7 +52,6 @@ #include "llviewercontrol.h" #include "llviewerwindow.h" #include "lldrawpoolwater.h" -#include "llagent.h" #include "llviewerregion.h" #include "llwlparamset.h" @@ -64,6 +63,7 @@ #include "curl/curl.h" LLWLParamManager * LLWLParamManager::sInstance = NULL; +static LLFastTimer::DeclareTimer FTM_UPDATE_WLPARAM("Update Windlight Params"); LLWLParamManager::LLWLParamManager() : @@ -292,7 +292,7 @@ void LLWLParamManager::updateShaderUniforms(LLGLSLShader * shader) void LLWLParamManager::propagateParameters(void) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_WLPARAM); + LLFastTimer ftm(FTM_UPDATE_WLPARAM); LLVector4 sunDir; LLVector4 moonDir; @@ -363,7 +363,7 @@ void LLWLParamManager::propagateParameters(void) void LLWLParamManager::update(LLViewerCamera * cam) { - LLFastTimer ftm(LLFastTimer::FTM_UPDATE_WLPARAM); + LLFastTimer ftm(FTM_UPDATE_WLPARAM); // update clouds, sun, and general mCurParams.updateCloudScrolling(); diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index f422791868..b0c3e8d711 100644 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -35,7 +35,7 @@ #include "pipeline.h" // library includes -#include "audioengine.h" // For MAX_BUFFERS for debugging. +#include "llaudioengine.h" // For MAX_BUFFERS for debugging. #include "imageids.h" #include "llerror.h" #include "llviewercontrol.h" @@ -138,6 +138,33 @@ BOOL gDebugPipeline = FALSE; LLPipeline gPipeline; const LLMatrix4* gGLLastMatrix = NULL; +LLFastTimer::DeclareTimer FTM_RENDER_GEOMETRY("Geometry"); +LLFastTimer::DeclareTimer FTM_RENDER_GRASS("Grass"); +LLFastTimer::DeclareTimer FTM_RENDER_INVISIBLE("Invisible"); +LLFastTimer::DeclareTimer FTM_RENDER_OCCLUSION("Occlusion"); +LLFastTimer::DeclareTimer FTM_RENDER_SHINY("Shiny"); +LLFastTimer::DeclareTimer FTM_RENDER_SIMPLE("Simple"); +LLFastTimer::DeclareTimer FTM_RENDER_TERRAIN("Terrain"); +LLFastTimer::DeclareTimer FTM_RENDER_TREES("Trees"); +LLFastTimer::DeclareTimer FTM_RENDER_UI("UI"); +LLFastTimer::DeclareTimer FTM_RENDER_WATER("Water"); +LLFastTimer::DeclareTimer FTM_RENDER_WL_SKY("Windlight Sky"); +LLFastTimer::DeclareTimer FTM_RENDER_ALPHA("Alpha Objects"); +LLFastTimer::DeclareTimer FTM_RENDER_CHARACTERS("Avatars"); +LLFastTimer::DeclareTimer FTM_RENDER_BUMP("Bump"); +LLFastTimer::DeclareTimer FTM_RENDER_FULLBRIGHT("Fullbright"); +LLFastTimer::DeclareTimer FTM_RENDER_GLOW("Glow"); +LLFastTimer::DeclareTimer FTM_GEO_UPDATE("Geo Update"); +LLFastTimer::DeclareTimer FTM_POOLRENDER("RenderPool"); +LLFastTimer::DeclareTimer FTM_POOLS("Pools"); +LLFastTimer::DeclareTimer FTM_RENDER_BLOOM_FBO("First FBO"); +LLFastTimer::DeclareTimer FTM_STATESORT("Sort Draw State"); +LLFastTimer::DeclareTimer FTM_PIPELINE("Pipeline"); +LLFastTimer::DeclareTimer FTM_CLIENT_COPY("Client Copy"); + +static LLFastTimer::DeclareTimer FTM_STATESORT_DRAWABLE("Sort Drawables"); +static LLFastTimer::DeclareTimer FTM_STATESORT_POSTSORT("Post Sort"); + //---------------------------------------- std::string gPoolNames[] = { @@ -973,7 +1000,7 @@ void LLPipeline::allocDrawable(LLViewerObject *vobj) void LLPipeline::unlinkDrawable(LLDrawable *drawable) { - LLFastTimer t(LLFastTimer::FTM_PIPELINE); + LLFastTimer t(FTM_PIPELINE); assertInitialized(); @@ -1035,7 +1062,7 @@ U32 LLPipeline::addObject(LLViewerObject *vobj) void LLPipeline::createObjects(F32 max_dtime) { - LLFastTimer ftm(LLFastTimer::FTM_GEO_UPDATE); + LLFastTimer ftm(FTM_GEO_UPDATE); LLMemType mt(LLMemType::MTYPE_PIPELINE_CREATE_OBJECTS); LLTimer update_timer; @@ -1201,9 +1228,12 @@ void LLPipeline::updateMovedList(LLDrawable::drawable_vector_t& moved_list) } } +static LLFastTimer::DeclareTimer FTM_OCTREE_BALANCE("Balance Octree"); +static LLFastTimer::DeclareTimer FTM_UPDATE_MOVE("Update Move"); + void LLPipeline::updateMove() { - LLFastTimer t(LLFastTimer::FTM_UPDATE_MOVE); + LLFastTimer t(FTM_UPDATE_MOVE); LLMemType mt_um(LLMemType::MTYPE_PIPELINE_UPDATE_MOVE); if (gSavedSettings.getBOOL("FreezeTime")) @@ -1249,7 +1279,7 @@ void LLPipeline::updateMove() //balance octrees { - LLFastTimer ot(LLFastTimer::FTM_OCTREE_BALANCE); + LLFastTimer ot(FTM_OCTREE_BALANCE); for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin(); iter != LLWorld::getInstance()->getRegionList().end(); ++iter) @@ -1354,10 +1384,11 @@ BOOL LLPipeline::getVisibleExtents(LLCamera& camera, LLVector3& min, LLVector3& return res; } +static LLFastTimer::DeclareTimer FTM_CULL("Object Culling"); void LLPipeline::updateCull(LLCamera& camera, LLCullResult& result, S32 water_clip) { - LLFastTimer t(LLFastTimer::FTM_CULL); + LLFastTimer t(FTM_CULL); LLMemType mt_uc(LLMemType::MTYPE_PIPELINE_UPDATE_CULL); grabReferences(result); @@ -1572,7 +1603,7 @@ void LLPipeline::updateGeom(F32 max_dtime) LLMemType mt(LLMemType::MTYPE_PIPELINE_UPDATE_GEOM); LLPointer<LLDrawable> drawablep; - LLFastTimer t(LLFastTimer::FTM_GEO_UPDATE); + LLFastTimer t(FTM_GEO_UPDATE); assertInitialized(); @@ -1840,6 +1871,8 @@ void LLPipeline::markRebuild(LLDrawable *drawablep, LLDrawable::EDrawableFlags f } } +static LLFastTimer::DeclareTimer FTM_RESET_DRAWORDER("Reset Draw Order"); + void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) { const U32 face_mask = (1 << LLPipeline::RENDER_TYPE_AVATAR) | @@ -1852,11 +1885,11 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) if (mRenderTypeMask & face_mask) { //clear faces from face pools - LLFastTimer t(LLFastTimer::FTM_RESET_DRAWORDER); + LLFastTimer t(FTM_RESET_DRAWORDER); gPipeline.resetDrawOrders(); } - LLFastTimer ftm(LLFastTimer::FTM_STATESORT); + LLFastTimer ftm(FTM_STATESORT); LLMemType mt(LLMemType::MTYPE_PIPELINE_STATE_SORT); //LLVertexBuffer::unbind(); @@ -1912,7 +1945,7 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) } { - LLFastTimer ftm(LLFastTimer::FTM_STATESORT_DRAWABLE); + LLFastTimer ftm(FTM_STATESORT_DRAWABLE); for (LLCullResult::drawable_list_t::iterator iter = sCull->beginVisibleList(); iter != sCull->endVisibleList(); ++iter) { @@ -1925,7 +1958,7 @@ void LLPipeline::stateSort(LLCamera& camera, LLCullResult &result) } { - LLFastTimer ftm(LLFastTimer::FTM_CLIENT_COPY); + LLFastTimer ftm(FTM_CLIENT_COPY); LLVertexBuffer::clientCopy(); } @@ -2188,7 +2221,7 @@ void renderSoundHighlights(LLDrawable* drawablep) void LLPipeline::postSort(LLCamera& camera) { LLMemType mt(LLMemType::MTYPE_PIPELINE_POST_SORT); - LLFastTimer ftm(LLFastTimer::FTM_STATESORT_POSTSORT); + LLFastTimer ftm(FTM_STATESORT_POSTSORT); assertInitialized(); @@ -2382,7 +2415,7 @@ void LLPipeline::postSort(LLCamera& camera) void render_hud_elements() { LLMemType mt_rhe(LLMemType::MTYPE_PIPELINE_RENDER_HUD_ELS); - LLFastTimer t(LLFastTimer::FTM_RENDER_UI); + LLFastTimer t(FTM_RENDER_UI); gPipeline.disableLights(); LLGLDisable fog(GL_FOG); @@ -2495,7 +2528,7 @@ void LLPipeline::renderHighlights() void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate) { LLMemType mt(LLMemType::MTYPE_PIPELINE_RENDER_GEOM); - LLFastTimer t(LLFastTimer::FTM_RENDER_GEOMETRY); + LLFastTimer t(FTM_RENDER_GEOMETRY); assertInitialized(); @@ -2594,7 +2627,7 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate) } else { - LLFastTimer t(LLFastTimer::FTM_POOLS); + LLFastTimer t(FTM_POOLS); // HACK: don't calculate local lights if we're rendering the HUD! // Removing this check will cause bad flickering when there are @@ -2626,7 +2659,7 @@ void LLPipeline::renderGeom(LLCamera& camera, BOOL forceVBOUpdate) pool_set_t::iterator iter2 = iter1; if (hasRenderType(poolp->getType()) && poolp->getNumPasses() > 0) { - LLFastTimer t(LLFastTimer::FTM_POOLRENDER); + LLFastTimer t(FTM_POOLRENDER); gGLLastMatrix = NULL; glLoadMatrixd(gGLModelView); @@ -2760,9 +2793,9 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera) LLAppViewer::instance()->pingMainloopTimeout("Pipeline:RenderGeomDeferred"); LLMemType mt_rgd(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED); - LLFastTimer t(LLFastTimer::FTM_RENDER_GEOMETRY); + LLFastTimer t(FTM_RENDER_GEOMETRY); - LLFastTimer t2(LLFastTimer::FTM_POOLS); + LLFastTimer t2(FTM_POOLS); LLGLEnable cull(GL_CULL_FACE); @@ -2804,7 +2837,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera) pool_set_t::iterator iter2 = iter1; if (hasRenderType(poolp->getType()) && poolp->getNumDeferredPasses() > 0) { - LLFastTimer t(LLFastTimer::FTM_POOLRENDER); + LLFastTimer t(FTM_POOLRENDER); gGLLastMatrix = NULL; glLoadMatrixd(gGLModelView); @@ -2862,7 +2895,7 @@ void LLPipeline::renderGeomDeferred(LLCamera& camera) void LLPipeline::renderGeomPostDeferred(LLCamera& camera) { LLMemType mt_rgpd(LLMemType::MTYPE_PIPELINE_RENDER_GEOM_POST_DEF); - LLFastTimer t(LLFastTimer::FTM_POOLS); + LLFastTimer t(FTM_POOLS); U32 cur_type = 0; LLGLEnable cull(GL_CULL_FACE); @@ -2895,7 +2928,7 @@ void LLPipeline::renderGeomPostDeferred(LLCamera& camera) pool_set_t::iterator iter2 = iter1; if (hasRenderType(poolp->getType()) && poolp->getNumPostDeferredPasses() > 0) { - LLFastTimer t(LLFastTimer::FTM_POOLRENDER); + LLFastTimer t(FTM_POOLRENDER); gGLLastMatrix = NULL; glLoadMatrixd(gGLModelView); @@ -4979,6 +5012,7 @@ void LLPipeline::bindScreenToTexture() } +static LLFastTimer::DeclareTimer FTM_RENDER_BLOOM("Bloom"); void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) { LLMemType mt_ru(LLMemType::MTYPE_PIPELINE_RENDER_BLOOM); @@ -5012,7 +5046,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) gGL.setColorMask(true, true); - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BLOOM); + LLFastTimer ftm(FTM_RENDER_BLOOM); gGL.color4f(1,1,1,1); LLGLDepthTest depth(GL_FALSE); LLGLDisable blend(GL_BLEND); @@ -5087,7 +5121,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) { { - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BLOOM_FBO); + LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); mGlow[2].bindTarget(); mGlow[2].clear(); } @@ -5159,7 +5193,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); { - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BLOOM_FBO); + LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); mGlow[i%2].bindTarget(); mGlow[i%2].clear(); } @@ -5201,7 +5235,7 @@ void LLPipeline::renderBloom(BOOL for_snapshot, F32 zoom_factor, int subfield) if (LLRenderTarget::sUseFBO) { - LLFastTimer ftm(LLFastTimer::FTM_RENDER_BLOOM_FBO); + LLFastTimer ftm(FTM_RENDER_BLOOM_FBO); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); } @@ -6095,6 +6129,10 @@ glh::matrix4f scale_translate_to_fit(const LLVector3 min, const LLVector3 max) return ret; } +static LLFastTimer::DeclareTimer FTM_SHADOW_RENDER("Render Shadows"); +static LLFastTimer::DeclareTimer FTM_SHADOW_ALPHA("Alpha Shadow"); +static LLFastTimer::DeclareTimer FTM_SHADOW_SIMPLE("Simple Shadow"); + void LLPipeline::generateSunShadow(LLCamera& camera) { @@ -6343,7 +6381,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera) mShadowCamera[j+4] = shadow_cam; } - LLFastTimer t(LLFastTimer::FTM_SHADOW_RENDER); + LLFastTimer t(FTM_SHADOW_RENDER); stop_glerror(); @@ -6381,7 +6419,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera) gDeferredShadowProgram.bind(); { - LLFastTimer ftm(LLFastTimer::FTM_SHADOW_SIMPLE); + LLFastTimer ftm(FTM_SHADOW_SIMPLE); LLGLDisable test(GL_ALPHA_TEST); gGL.getTexUnit(0)->disable(); for (U32 i = 0; i < sizeof(types)/sizeof(U32); ++i) @@ -6392,7 +6430,7 @@ void LLPipeline::generateSunShadow(LLCamera& camera) } { - LLFastTimer ftm(LLFastTimer::FTM_SHADOW_ALPHA); + LLFastTimer ftm(FTM_SHADOW_ALPHA); LLGLEnable test(GL_ALPHA_TEST); gGL.setAlphaRejectSettings(LLRender::CF_GREATER, 0.6f); renderObjects(LLRenderPass::PASS_ALPHA_SHADOW, LLVertexBuffer::MAP_VERTEX | LLVertexBuffer::MAP_TEXCOORD0 | LLVertexBuffer::MAP_COLOR, TRUE); diff --git a/indra/newview/pipeline.h b/indra/newview/pipeline.h index fc02e7dd88..8f6867aa01 100644 --- a/indra/newview/pipeline.h +++ b/indra/newview/pipeline.h @@ -78,6 +78,27 @@ glh::matrix4f gl_ortho(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, glh::matrix4f gl_perspective(GLfloat fovy, GLfloat aspect, GLfloat zNear, GLfloat zFar); glh::matrix4f gl_lookat(LLVector3 eye, LLVector3 center, LLVector3 up); +extern LLFastTimer::DeclareTimer FTM_RENDER_GEOMETRY; +extern LLFastTimer::DeclareTimer FTM_RENDER_GRASS; +extern LLFastTimer::DeclareTimer FTM_RENDER_INVISIBLE; +extern LLFastTimer::DeclareTimer FTM_RENDER_OCCLUSION; +extern LLFastTimer::DeclareTimer FTM_RENDER_SHINY; +extern LLFastTimer::DeclareTimer FTM_RENDER_SIMPLE; +extern LLFastTimer::DeclareTimer FTM_RENDER_TERRAIN; +extern LLFastTimer::DeclareTimer FTM_RENDER_TREES; +extern LLFastTimer::DeclareTimer FTM_RENDER_UI; +extern LLFastTimer::DeclareTimer FTM_RENDER_WATER; +extern LLFastTimer::DeclareTimer FTM_RENDER_WL_SKY; +extern LLFastTimer::DeclareTimer FTM_RENDER_ALPHA; +extern LLFastTimer::DeclareTimer FTM_RENDER_CHARACTERS; +extern LLFastTimer::DeclareTimer FTM_RENDER_BUMP; +extern LLFastTimer::DeclareTimer FTM_RENDER_FULLBRIGHT; +extern LLFastTimer::DeclareTimer FTM_RENDER_GLOW; +extern LLFastTimer::DeclareTimer FTM_STATESORT; +extern LLFastTimer::DeclareTimer FTM_PIPELINE; +extern LLFastTimer::DeclareTimer FTM_CLIENT_COPY; + + class LLPipeline { public: diff --git a/indra/newview/skins/default/colors.xml b/indra/newview/skins/default/colors.xml index 73f0d32d12..c739d4d455 100644 --- a/indra/newview/skins/default/colors.xml +++ b/indra/newview/skins/default/colors.xml @@ -664,4 +664,11 @@ <color name="OutputMonitorMutedColor" reference="DkGray2" /> + <color + name="SysWellItemUnselected" + value="0 0 0 0" /> + <color + name="SysWellItemSelected" + value="0.3 0.3 0.3 1.0" /> + </colors> diff --git a/indra/newview/skins/default/textures/navbar/Favorite_Star_Active.png b/indra/newview/skins/default/textures/navbar/Favorite_Star_Active.png Binary files differindex a4060e5e37..e27dbe2cad 100644 --- a/indra/newview/skins/default/textures/navbar/Favorite_Star_Active.png +++ b/indra/newview/skins/default/textures/navbar/Favorite_Star_Active.png diff --git a/indra/newview/skins/default/textures/navbar/Favorite_Star_Off.png b/indra/newview/skins/default/textures/navbar/Favorite_Star_Off.png Binary files differindex 9c2815ebba..82d044d817 100644 --- a/indra/newview/skins/default/textures/navbar/Favorite_Star_Off.png +++ b/indra/newview/skins/default/textures/navbar/Favorite_Star_Off.png diff --git a/indra/newview/skins/default/textures/navbar/Favorite_Star_Over.png b/indra/newview/skins/default/textures/navbar/Favorite_Star_Over.png Binary files differindex 377b707529..7909d54f2b 100644 --- a/indra/newview/skins/default/textures/navbar/Favorite_Star_Over.png +++ b/indra/newview/skins/default/textures/navbar/Favorite_Star_Over.png diff --git a/indra/newview/skins/default/textures/navbar/Favorite_Star_Press.png b/indra/newview/skins/default/textures/navbar/Favorite_Star_Press.png Binary files differindex a69a729bd4..6670667022 100644 --- a/indra/newview/skins/default/textures/navbar/Favorite_Star_Press.png +++ b/indra/newview/skins/default/textures/navbar/Favorite_Star_Press.png diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml index 1515a34eeb..0325e755eb 100644 --- a/indra/newview/skins/default/textures/textures.xml +++ b/indra/newview/skins/default/textures/textures.xml @@ -381,6 +381,7 @@ <texture name="icon_groupnoticeinventory.tga"/> <texture name="icon_lock.tga"/> <texture name="icon_place.tga"/> + <texture name="icon_place_for_sale.tga"/> <texture name="icon_popular.tga"/> <texture name="icon_top_pick.tga"/> @@ -414,6 +415,8 @@ <texture name="inv_item_jacket.tga"/> <texture name="inv_item_landmark.tga"/> <texture name="inv_item_landmark_visited.tga"/> + <texture name="inv_item_linkitem.tga"/> + <texture name="inv_item_linkfolder.tga"/> <texture name="inv_item_notecard.tga"/> <texture name="inv_item_object.tga"/> <texture name="inv_item_object_multi.tga"/> @@ -527,5 +530,24 @@ <texture name="default_land_picture.j2c"/> <texture name="default_profile_picture.j2c"/> <texture name="locked_image.j2c"/> + + <texture name="media_btn_back.png"/> + <texture name="media_btn_done.png"/> + <texture name="media_btn_forward.png"/> + <texture name="media_btn_home.png"/> + <texture name="media_btn_newwindow.png"/> + <texture name="media_btn_optimalzoom.png"/> + <texture name="media_btn_reload.png"/> + <texture name="media_btn_scrolldown.png"/> + <texture name="media_btn_scrollleft.png"/> + <texture name="media_btn_scrollright.png"/> + <texture name="media_btn_scrollup.png"/> + <texture name="media_btn_stoploading.png"/> + <texture name="media_panel_divider.png"/> + + <texture name="media_floater_border_16.png" scale_top="12" scale_left="4" scale_bottom="4" scale_right="12"/> + + <texture name="media_panel_bg.png" preload="true" scale_left="9" scale_top="9" scale_right="9" scale_bottom="9" /> + <texture name="media_panel_hoverrectangle.png" preload="true" scale_left="9" scale_top="9" scale_right="9" scale_bottom="9" /> </textures> diff --git a/indra/newview/skins/default/textures/windows/Icon_Close_Foreground.png b/indra/newview/skins/default/textures/windows/Icon_Close_Foreground.png Binary files differindex fb59f2e61e..5dd0852a72 100644 --- a/indra/newview/skins/default/textures/windows/Icon_Close_Foreground.png +++ b/indra/newview/skins/default/textures/windows/Icon_Close_Foreground.png diff --git a/indra/newview/skins/default/textures/windows/Icon_Close_Press.png b/indra/newview/skins/default/textures/windows/Icon_Close_Press.png Binary files differindex 7177c803b0..ea547fca6f 100644 --- a/indra/newview/skins/default/textures/windows/Icon_Close_Press.png +++ b/indra/newview/skins/default/textures/windows/Icon_Close_Press.png diff --git a/indra/newview/skins/default/textures/windows/Icon_Dock_Foreground.png b/indra/newview/skins/default/textures/windows/Icon_Dock_Foreground.png Binary files differindex cadd6bc8ce..4207ba68e5 100644 --- a/indra/newview/skins/default/textures/windows/Icon_Dock_Foreground.png +++ b/indra/newview/skins/default/textures/windows/Icon_Dock_Foreground.png diff --git a/indra/newview/skins/default/textures/windows/Icon_Dock_Press.png b/indra/newview/skins/default/textures/windows/Icon_Dock_Press.png Binary files differindex 08f8cfc3d4..2d09475783 100644 --- a/indra/newview/skins/default/textures/windows/Icon_Dock_Press.png +++ b/indra/newview/skins/default/textures/windows/Icon_Dock_Press.png diff --git a/indra/newview/skins/default/textures/windows/Icon_Undock_Foreground.png b/indra/newview/skins/default/textures/windows/Icon_Undock_Foreground.png Binary files differindex d790cf57ef..9a71d16a3f 100644 --- a/indra/newview/skins/default/textures/windows/Icon_Undock_Foreground.png +++ b/indra/newview/skins/default/textures/windows/Icon_Undock_Foreground.png diff --git a/indra/newview/skins/default/textures/windows/Icon_Undock_Press.png b/indra/newview/skins/default/textures/windows/Icon_Undock_Press.png Binary files differindex fdae4b75f6..3ab8c3666a 100644 --- a/indra/newview/skins/default/textures/windows/Icon_Undock_Press.png +++ b/indra/newview/skins/default/textures/windows/Icon_Undock_Press.png diff --git a/indra/newview/skins/default/xui/da/floater_world_map.xml b/indra/newview/skins/default/xui/da/floater_world_map.xml index f058cf0468..53c53dd707 100644 --- a/indra/newview/skins/default/xui/da/floater_world_map.xml +++ b/indra/newview/skins/default/xui/da/floater_world_map.xml @@ -52,6 +52,6 @@ <button label="Vis destination" label_selected="Vis destination" name="Show Destination" tool_tip="Centrér kortet på valgte lokation"/> <button label="Slet" label_selected="Slet" name="Clear" tool_tip="Stop søg"/> <button label="Vis min position" label_selected="Vis min position" name="Show My Location" tool_tip="Centrer kortet på din avatars lokation"/> - <button label="Kopiér SLURL til udklipsholder" name="copy_slurl" tool_tip="Kopierer den nuværende lokation som et SLURL, så det kan bruges på nettet."/> + <button label="Kopiér SLurl til udklipsholder" name="copy_slurl" tool_tip="Kopierer den nuværende lokation som et SLurl, så det kan bruges på nettet."/> <slider label="Zoom" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/da/notifications.xml b/indra/newview/skins/default/xui/da/notifications.xml index eedc588449..633fff079b 100644 --- a/indra/newview/skins/default/xui/da/notifications.xml +++ b/indra/newview/skins/default/xui/da/notifications.xml @@ -1,15 +1,14 @@ -<?xml version="1.0" ?><notifications> - - <global name="skipnexttime"> - +<?xml version="1.0" encoding="utf-8"?> +<notifications> + <global name="skipnexttime"> Vis ikke dette igen - </global> - - <global name="alwayschoose"> - + </global> + <global name="alwayschoose"> Vælg altid dette - </global> - + </global> + <global name="implicitclosebutton"> + Luk + </global> <template name="okbutton"> <form> <button @@ -74,421 +73,438 @@ text="$canceltext"/> </form> </template> + <notification functor="GenericAcknowledge" label="Ukendt advarsels-besked" name="MissingAlert"> + Din version af Second Life kan ikke vise den advarselsbesked den modtog. - <notification - functor="GenericAcknowledge" - - name="MissingAlert" - > -'[_NAME]' mangler fra notifications.xml. - <usetemplate - name="okbutton" - yestext="OK"/> - </notification> - - <notification - - name="FloaterNotFound" - > -Floater error: Kunne ikke finde følgende kontrol: +Fejl detaljer: Advarslen '[_NAME]' blev ikke fundet i notifications.xml. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="FloaterNotFound"> + Floater error: Kunne ikke finde følgende kontrol: [CONTROLS] + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="TutorialNotFound"> + Der er i øjeblikket ingen tilgængelig guide. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="GenericAlert"> + [MESSAGE] + </notification> + <notification name="GenericAlertYesCancel"> + [MESSAGE] + <usetemplate name="okcancelbuttons" notext="Annullér" yestext="Ja"/> + </notification> + <notification name="BadInstallation"> + Der opstod en fejl ved opdatering af Second Life. Hent venligst den nyeste version fra secondlife.com. <usetemplate name="okbutton" yestext="OK"/> - </notification> - - <notification - - name="TutorialNotFound" - > -Der er i øjeblikket ingen tilgængelig guide. - <usetemplate + </notification> + <notification name="LoginFailedNoNetwork"> + Netværksfejl: Kunne ikke oprette forbindelse. +'[DIAGNOSTIC]' +Check venligst din netværksforbindelse. + <usetemplate name="okbutton" yestext="OK"/> - </notification> - - <notification - - name="GenericAlert" - > -[MESSAGE] - </notification> - - <notification - - name="GenericAlertYesCancel" - > -[MESSAGE] - <usetemplate - name="okcancelbuttons" - notext="Annullér" - yestext="Ja"/> - </notification> - - <notification - - name="WearableSave" - > -Gem ændringer til nuværende tøj/krops del? - <usetemplate - canceltext="Annullér" - name="yesnocancelbuttons" - notext="Gem ikke" - yestext="Gem"/> - </notification> - - <notification - - name="CompileQueueSaveText" - > -Der var problemer med upload af teksten til et script af følgende årsager: [REASON]. Prøv igen senere. - </notification> - - <notification - - name="CompileQueueSaveBytecode" - > -Der var problemer med at uploade den kompileret script af følgende årsager: [REASON]. Prøv igen senere. - </notification> - - <notification - - name="WriteAnimationFail" - > -Der var et problem med skrivning af animations data. Prøv igen senere. - </notification> - - <notification - - name="UploadAuctionSnapshotFail" - > -Der var problemer med at uploade billedet til auktionen af følgende årsager: [REASON] - </notification> - - <notification - - name="UnableToViewContentsMoreThanOne" - > -Ude af stand til at se indholdet af mere end ét element ad gangen. + </notification> + <notification name="MessageTemplateNotFound"> + Besked template [PATH] kunne ikke findes. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + <notification name="WearableSave"> + Gem ændringer til nuværende tøj/krops del? + <usetemplate canceltext="Annullér" name="yesnocancelbuttons" notext="Gem ikke" yestext="Gem"/> + </notification> + <notification name="CompileQueueSaveText"> + Der var problemer med upload af teksten til et script af følgende årsager: [REASON]. Prøv igen senere. + </notification> + <notification name="CompileQueueSaveBytecode"> + Der var problemer med at uploade den kompileret script af følgende årsager: [REASON]. Prøv igen senere. + </notification> + <notification name="WriteAnimationFail"> + Der var et problem med skrivning af animations data. Prøv igen senere. + </notification> + <notification name="UploadAuctionSnapshotFail"> + Der var problemer med at uploade billedet til auktionen af følgende årsager: [REASON] + </notification> + <notification name="UnableToViewContentsMoreThanOne"> + Ude af stand til at se indholdet af mere end ét element ad gangen. Vælg kun en genstand, og prøv igen. - </notification> - - <notification - - name="SaveClothingBodyChanges" - > -Gem alle ændringer til tøj/krops dele? - <usetemplate - canceltext="Annullér" - name="yesnocancelbuttons" - notext="Gem Ikke" - yestext="Gem Alt"/> - </notification> - - <notification - - name="GrantModifyRights" - > -At give redigerings rettigheder til en anden beboer, giver dem mulighed for at ændre, slette eller tage alle genstande, du måtte have i verden. Vær meget forsigtig når uddeler denne tilladelse. + </notification> + <notification name="SaveClothingBodyChanges"> + Gem alle ændringer til tøj/krops dele? + <usetemplate canceltext="Annullér" name="yesnocancelbuttons" notext="Gem Ikke" yestext="Gem Alt"/> + </notification> + <notification name="GrantModifyRights"> + At give redigerings rettigheder til en anden beboer, giver dem mulighed for at ændre, slette eller tage alle genstande, du måtte have i verden. Vær meget forsigtig når uddeler denne tilladelse. Ønsker du at ændre rettigheder for [FIRST_NAME] [LAST_NAME]? - <usetemplate - name="okcancelbuttons" - notext="Nej" - yestext="Ja"/> - </notification> - - <notification - - name="GrantModifyRightsMultiple" - > -At give redigerings rettigheder til en anden beboer, giver dem mulighed for at ændre, slette eller tage alle genstande, du måtte have i verden. Vær meget forsigtig når uddeler denne tilladelse. + <usetemplate name="okcancelbuttons" notext="Nej" yestext="Ja"/> + </notification> + <notification name="GrantModifyRightsMultiple"> + At give redigerings rettigheder til en anden beboer, giver dem mulighed for at ændre, slette eller tage alle genstande, du måtte have i verden. Vær meget forsigtig når uddeler denne tilladelse. Ønsker du at ændre rettigheder for de valgte beboere? - <usetemplate - name="okcancelbuttons" - notext="Nej" - yestext="Ja"/> - </notification> - - <notification - - name="RevokeModifyRights" - > -Vil du tilbagekalde rettighederne for [FIRST_NAME] [LAST_NAME]? - <usetemplate - name="okcancelbuttons" - notext="Nej" - yestext="Ja"/> - </notification> - - <notification - - name="RevokeModifyRightsMultiple" - > -Vil du tilbagekalde rettighederne for de valgte beboere? - <usetemplate - name="okcancelbuttons" - notext="Nej" - yestext="Ja"/> - </notification> - - <notification - - name="UnableToCreateGroup" - > -Kunne ikke oprette gruppe. + <usetemplate name="okcancelbuttons" notext="Nej" yestext="Ja"/> + </notification> + <notification name="RevokeModifyRights"> + Vil du tilbagekalde rettighederne for [FIRST_NAME] [LAST_NAME]? + <usetemplate name="okcancelbuttons" notext="Nej" yestext="Ja"/> + </notification> + <notification name="RevokeModifyRightsMultiple"> + Vil du tilbagekalde rettighederne for de valgte beboere? + <usetemplate name="okcancelbuttons" notext="Nej" yestext="Ja"/> + </notification> + <notification name="UnableToCreateGroup"> + Kunne ikke oprette gruppe. [MESSAGE] - <usetemplate - name="okbutton" - yestext="OK"/> - </notification> - - <notification - - name="PanelGroupApply" - > -[NEEDS_APPLY_MESSAGE] + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="PanelGroupApply"> + [NEEDS_APPLY_MESSAGE] [WANT_APPLY_MESSAGE] - <usetemplate - canceltext="Annullér" - name="yesnocancelbuttons" - notext="Ignorer Ændringer" - yestext="Godkend Ændringer"/> - </notification> - - <notification - - name="MustSpecifyGroupNoticeSubject" - > -Du skal angive et emne for at sende en gruppe besked. - <usetemplate - name="okbutton" - yestext="OK"/> - </notification> - - <notification - - name="AddGroupOwnerWarning" - > -Du er ved at tilføje medlemmer til rollen som [ROLE_NAME]. + <usetemplate canceltext="Annullér" name="yesnocancelbuttons" notext="Ignorer Ændringer" yestext="Godkend Ændringer"/> + </notification> + <notification name="MustSpecifyGroupNoticeSubject"> + Du skal angive et emne for at sende en gruppe besked. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="AddGroupOwnerWarning"> + Du er ved at tilføje medlemmer til rollen som [ROLE_NAME]. Medlemmer ikke kan fjernes fra denne rolle. Medlemmerne skal fratræde sin rolle selv. Er du sikker på du vil fortsætte? - <usetemplate - ignoretext="Når du tilføjer medlemmer til ejer rollen" - name="okcancelignore" - notext="Nej" - yestext="Ja"/> - </notification> - - <notification - - name="AssignDangerousActionWarning" - > -Du er ved at tilføje muligheden for '[ACTION_NAME]' til + <usetemplate ignoretext="Når du tilføjer medlemmer til ejer rollen" name="okcancelignore" notext="Nej" yestext="Ja"/> + </notification> + <notification name="AssignDangerousActionWarning"> + Du er ved at tilføje muligheden for '[ACTION_NAME]' til rollen '[ROLE_NAME]'. *ADVARSEL* Ethvert medlem i en rolle med denne evne kan tildele sig selv -- og et andet medlem - roller med flere beføjelser, end de har i øjeblikket, potentielt kan de ophøje sig selv til nær-Ejer magt. Være sikker på, at du ved, hvad du laver, før tildeling af denne evne. Add this Ability to '[ROLE_NAME]'? + <usetemplate name="okcancelbuttons" notext="Nej" yestext="Ja"/> + </notification> + <notification name="ClickSoundHelpLand"> + Media og musik kan kun ses og høres indenfor parcellen. Lyd og stemme valg muligheder kan begrænses til parcellen eller de kan høres af beboere udenfor parcellen, afhængigt af deres indholdsrating. Gå til 'Knowledge Base' for at lære hvordan disse valg opsættes. + <url name="url"> + https://support.secondlife.com/ics/support/default.asp?deptID=4417&task=knowledge&questionID=5046 + </url> <usetemplate name="okcancelbuttons" - notext="Nej" - yestext="Ja"/> - </notification> - - <notification - - name="ClickPublishHelpAvatar" - > -Hvis du vælger "Vis i Søgning" Vises: + yestext="Gå til 'Knowledge Base'" + notext="Luk" /> + </notification> + <notification name="ClickSearchHelpAll"> + Søgeresultater er organiseret baseret på den fane du står på, din indholdsrating, den valgte kategori og andre faktorer. for yderligere detaljer se i 'Knowledge Base'. + <url name="url"> + https://support.secondlife.com/ics/support/default.asp?deptID=4417&task=knowledge&questionID=4722 + </url> + <usetemplate + name="okcancelbuttons" + yestext="Gå til 'Knowledge Base'" + notext="Luk" /> + </notification> + <notification name="ClickPublishHelpAvatar"> + Hvis du vælger "Vis i Søgning" Vises: - Din profil i søgeresultater - Et link til din profile i de offentlige gruppe sider - </notification> - - <notification - - name="ClickWebProfileHelpAvatar" - > -Hvis en beboer har en hjemmeside adresse kan du: + </notification> + <notification name="ClickUploadHelpPermissions"> + Dinne standard rettigheder virker muligvis ikke i ældre regioner. + </notification> + <notification name="ClickWebProfileHelpAvatar"> + Hvis en beboer har en hjemmeside adresse kan du: * Klikke 'Load' for at side deres side her. * Klikke Load > 'I ekstern browser' for at se siden i din standard browser. * Klikke Load > 'Hjemme URL' for at returnere til denne beboers side hvis du har navigeret væk. Når du ser din egen profil, kan du skrive hvilken som helst adresse og klikke ok for at få den vist i din egen profil. Andre beboere kan besøge den adresse du har sat, når de besøger din profil. - </notification> - - <notification - - name="JoinGroupCannotAfford" - > -Tilmelding til denne gruppe koster L$[COST]. + </notification> + <notification name="JoinGroupCannotAfford"> + Tilmelding til denne gruppe koster L$[COST]. Du har ikke nok L$ til denne tilmelding. - </notification> - - <notification - - name="PromptMissingSubjMsg" - > -E-mail dette billede med standard emne eller besked? - <usetemplate - name="okcancelbuttons" - notext="Annullér" - yestext="OK"/> - </notification> - - <notification - - name="ErrorUploadingPostcard" - > -Der var et problem med at sende billedet på grund af følgende: [REASON] - </notification> - - <notification - - name="MustHaveAccountToLogIn" - > -Ups! Noget var tomt. + </notification> + <notification name="PromptMissingSubjMsg"> + E-mail dette billede med standard emne eller besked? + <usetemplate name="okcancelbuttons" notext="Annullér" yestext="OK"/> + </notification> + <notification name="ErrorUploadingPostcard"> + Der var et problem med at sende billedet på grund af følgende: [REASON] + </notification> + <notification name="MustHaveAccountToLogIn"> + Ups! Noget var tomt. Du skal skrive både fornavn og efternavn på din figur. Du har brug for en konto for at logge ind i [SECOND_LIFE]. Vil du oprette en nu? - <usetemplate - name="okcancelbuttons" - notext="Prøv igen" - yestext="Lav ny konto"/> - </notification> - - <notification - - name="ResetShowNextTimeDialogs" - > -Vil du gerne genaktivere alle disse popups, som du tidligere har bedt om ikke at få vist? - <usetemplate - name="okcancelbuttons" - notext="Annullér" - yestext="OK"/> - </notification> - - <notification - - name="SkipShowNextTimeDialogs" - > -Vil du deaktivere alle popups som kan undværes? - <usetemplate - name="okcancelbuttons" - notext="Annullér" - yestext="OK"/> - </notification> - - <notification - - name="ChangeSkin" - > -Det nye udseende vil vises efter du har genstartet [SECOND_LIFE]. - </notification> - - <notification - - name="UnsupportedHardware" - > - <form name="form"> - <ignore name="ignore" - text="Ved opdagelse af ikke supporteret hardware"/> - </form> - </notification> - - <notification - - name="UnknownGPU" - > - <form name="form"> - <ignore name="ignore" - text="Ved opdagelse af et ukendt grafikkort"/> - </form> - </notification> - - <notification - - name="CannotGiveCategory" - > -Du har ikke tilladelse til at videreføre den valgte mappe. - </notification> - - <notification - - name="CannotBuyLandNoRegion" - > -Ikke i stand til at købe land: + <usetemplate name="okcancelbuttons" notext="Prøv igen" yestext="Lav ny konto"/> + </notification> + <notification name="ResetShowNextTimeDialogs"> + Vil du gerne genaktivere alle disse popups, som du tidligere har bedt om ikke at få vist? + <usetemplate name="okcancelbuttons" notext="Annullér" yestext="OK"/> + </notification> + <notification name="SkipShowNextTimeDialogs"> + Vil du deaktivere alle popups som kan undværes? + <usetemplate name="okcancelbuttons" notext="Annullér" yestext="OK"/> + </notification> + <notification name="ChangeSkin"> + Det nye udseende vil vises efter du har genstartet [SECOND_LIFE]. + </notification> + <notification name="UnsupportedHardware"/> + <notification name="UnknownGPU"> + <form name="form"> + <ignore name="ignore" text="Ved opdagelse af et ukendt grafikkort"/> + </form> + </notification> + + <notification name="invalid_tport"> +Der er problemer med at håndtere din teleport. Det kan være nødvendigt at logge ud og ind for at kunne skifte teleportere. +Hvis du bliver ved med at have problemet kan du checke teknisk support på: +www.secondlife.com/support + </notification> + <notification name="invalid_region_handoff"> +Problem registreret i forbindelse med skift til ny region. Det kan være nødvendigt at logge ud og ind for at kunne skifte regioner. +Hvis du bliver ved med at have problemet kan du checke teknisk support på: +www.secondlife.com/support + </notification> + <notification name="blocked_tport"> +Beklager, teleport er blokeret lige nu. Prøv igen senere. +Hvis du stadig ikke kan teleporte, prøv venligst at logge ud og ligge ind for at løse dette problem. + </notification> + <notification name="nolandmark_tport"> +Beklager, systemet kunne ikke finde landmærke destinationen. + </notification> + <notification name="timeout_tport"> +Beklager, systemet kunne ikke fuldføre teleport forbindelse. +Prøv igen om lidt. + </notification> + <notification name="noaccess_tport"> +Beklager, du har ikke adgang til denne teleport destination. + </notification> + <notification name="missing_attach_tport"> +Dine vedhæng er ikke ankommet endnu. Prøv at vente lidt endnu eller log ud og ind igen før du prøver at teleporte igen. + </notification> + <notification name="too_many_uploads_tport"> +Tekniske problemer hindrer at din teleport kan gennemføres. +Prøv venligst igen om lidt eller vælg et mindre travlt område. + </notification> + <notification name="expired_tport"> +Beklager, men systemet kunne ikke fuldføre din teleport i rimelig tid. Prøv venligst igen om lidt. + </notification> + <notification name="expired_region_handoff"> +Beklager, men systemet kunne ikke fuldføre skift til anden region i rimelig tid. Prøv venligst igen om lidt. + </notification> + <notification name="no_host"> +Ikke muligt at fine teleport destination. Destinationen kan være midlertidig utilgængelig eller findes ikke mere. +Prøv evt. igen om lidt. + </notification> + <notification name="no_inventory_host"> +Beholdningssystemet er ikke tilgængelig lige nu. + </notification> + + <notification name="CannotGiveCategory"> + Du har ikke tilladelse til at videreføre den valgte mappe. + </notification> + <notification name="CannotEncodeFile"> + Kunne ikke 'forstå' filen: [FILE] + </notification> + <notification name="CannotBuyLandNoRegion"> + Ikke i stand til at købe land: Kan ikke finde region som dette land er i. - </notification> - - <notification - - name="YouHaveBeenLoggedOut" - > -Du er blevet logget ud af [SECOND_LIFE]: + </notification> + <notification name="ShowOwnersHelp"> + Vis ejere: +Farver på parceller viser ejer-type. + +Grøn = Dit land +Turkis = Din gruppes land +Rød = Ejet af andre +Gul = Til salg +Lilla = På auktion +Grå = Offentligt ejet + </notification> + <notification name="YouHaveBeenLoggedOut"> + Du er blevet logget ud af [SECOND_LIFE]: [MESSAGE] Du kan stadig se eksiterende PB'er og chat ved at klikke'Se PB & Chat'. Ellers, klik 'Afslut' for at afslutte [SECOND_LIFE] nu. - <usetemplate - name="okcancelbuttons" - notext="Quit" - yestext="Se IM & Chat"/> - </notification> - - <notification - - label="Add Friend" - name="AddFriend" - > -Venner kan give tilladelse til at følge hinanden + <usetemplate name="okcancelbuttons" notext="Afslut" yestext="Se PB & Chat"/> + </notification> + <notification label="Tilføj ven" name="AddFriend"> + Venner kan give tilladelse til at følge hinanden på Verdenskortet eller modtage status opdateringer. Tilbyd venskab til [NAME]? - <usetemplate - name="okcancelbuttons" - notext="Annullér" - yestext="OK"/> - </notification> - - <notification - - label="Add Friend" - name="AddFriendWithMessage" - > -Venner kan give tilladelse til at følge hinanden + <usetemplate name="okcancelbuttons" notext="Annullér" yestext="OK"/> + </notification> + <notification label="Tilføj ven" name="AddFriendWithMessage"> + Venner kan give tilladelse til at følge hinanden på Verdenskortet eller modtage status opdateringer. Tilbyd venskab til [NAME]? - <form name="form"> - <input name="message" type="text"> -Vil du være min ven? - </input> + <form name="form"> + <input name="message" type="text"> + Vil du være min ven? + </input> + <button name="Offer" text="OK"/> + <button name="Cancel" text="Annullér"/> + </form> + </notification> + <notification name="NotEnoughCurrency"> + [NAME] L$ [PRICE] Du har ikke nok L$ til dette. + </notification> + <notification name="BuyOneObjectOnly"> + Ikke muligt at købe mere end et objekt ad gangen. Vælg kun ét objekt og prøv igen. + </notification> + <notification name="CannotStartAuctionAlreadyForSale"> + Du kan ikke starte en auktion på en parcel som allerede er sat til salg. Fjern 'til salg' muligheden hvis du ønsker at starte en auktion. + </notification> + <notification name="OfferTeleport"> + <form name="form"> + <input name="message" type="text"> + Mød mig i [REGION] + </input> + <button name="OK" text="OK"/> + <button name="Cancel" text="Annullér"/> + </form> + </notification> + <notification name="RegionEntryAccessBlocked"> + Du har ikke adgang til denne region på grund af din valgte indholdsrating. Dette kan skyldes manglende validering af din alder. + +Undersøg venligst om du har installeret den nyeste Second Life klient, og gå til 'Knowledge Base' for yderligere detaljer om adgang til områder med denne indholdsrating. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + <notification name="RegionEntryAccessBlocked_KB"> + Du har ikke adgang til denne region på grund af din valgte indholdsrating. + +Gå til 'Knowledge Base' for mere information om indholdsratings. + <url name="url"> + https://support.secondlife.com/ics/support/default.asp?deptID=4417&task=knowledge&questionID=6010 + </url> + <usetemplate + name="okcancelignore" + yestext="Gå til 'Knowledge Base'" + notext="Luk" + ignoretext="Når regionen er blokeret på grund af indholdsrating"/> + </notification> + <notification name="RegionEntryAccessBlocked_Notify"> + Du har ikke adgang til denne region på grund af din valgte indholdsrating. + </notification> + <notification name="RegionEntryAccessBlocked_Change"> + Du har ikke adgang til denne region på grund af din nuværende indholdsrating opsætning. + +Du kan vælge 'Indstillinger' for at hæve din indholdsrating nu og dermed få adgang. Du vil så få mulighed for at søge og få adgang til områder med indhold af typen [REGIONMATURITY]. Hvis du senere ønsker at skifte tilbage, kan du skifte tilbage i 'Indstillinger'. + <form name="form"> <button - - - name="Offer" - text="OK"/> + name="OK" + text="Ændre præferencer"/> <button - name="Cancel" - text="Annullér"/> + text="Luk"/> + <ignore name="ignore" text="Når regionen er blokeret på grund af indholdsrating"/> </form> - </notification> + </notification> + <notification name="LandClaimAccessBlocked"> + Du kan ikke kræve dette land på grund af din nuværende indholdsrating indstillinge . Dette kan skyldes manglende validering af din alder. - <notification - - name="NotEnoughCurrency" - > -[NAME] L$ [PRICE] Du har ikke nok L$ til dette. - </notification> +Undersøg om du har den nyeste Second Life klient og gå venligst til 'Knowledge Base' for yderligere detaljer om adgang til områder med denne indholdsrating. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + <notification name="LandClaimAccessBlocked_KB"> + Du kan ikke kræve dette land på grund af din nuværende indholdsrating indstilling.. + +Gå venligst til 'Knowledge Base' for yderligere information om indholdsrating. + <url name="url"> + https://support.secondlife.com/ics/support/default.asp?deptID=4417&task=knowledge&questionID=6010 + </url> + <usetemplate + name="okcancelignore" + yestext="Gå til 'Knowledge Base'" + notext="Luk" + ignoretext="Når land ikke kan kræves på grund af indholdsrating"/> + </notification> + <notification name="LandClaimAccessBlocked_Notify"> + Du kan ikke kræve dette land på grund af din indholdsrating. + </notification> + <notification name="LandClaimAccessBlocked_Change"> + Du kan ikke kræve dette land på grund af din nuværende indholdsrating indstilling.. + +Du kan vælge 'Indstillinger' for at hæve din indholdsrating nu og dermed få adgang. Du vil så få mulighed for at søge og få adgang til områder med indhold af typen [REGIONMATURITY]. Hvis du senere ønsker at skifte tilbage, kan du skifte tilbage i 'Indstillinger'. + <usetemplate + name="okcancelignore" + yestext="Ændre præferencer" + notext="Luk" + ignoretext="Når land ikke kan kræves på grund af indholdsrating"/> + </notification> + <notification name="LandBuyAccessBlocked"> + Du kan ikke købe dette land på grund af din nuværende indholdsrating indstillinge . Dette kan skyldes manglende validering af din alder. + +Undersøg om du har den nyeste Second Life klient og gå venligst til 'Knowledge Base' for yderligere detaljer om adgang til områder med denne indholdsrating. + <usetemplate + name="okbutton" + yestext="OK"/> + </notification> + <notification name="LandBuyAccessBlocked_KB"> + Du kan ikke købe dette land på grund af din nuværende indholdsrating. + +Gå til 'Knowledge Base' for yderligere detaljer om indholdsrating. + <url name="url"> + https://support.secondlife.com/ics/support/default.asp?deptID=4417&task=knowledge&questionID=6010 + </url> + <usetemplate + name="okcancelignore" + yestext="Gå til 'Knowledge Base'" + notext="Luk" + ignoretext="Når land ikke kan købes på grund af indholdsrating"/> + </notification> + <notification name="LandBuyAccessBlocked_Notify"> + Du kan ikke købe dette land på grund af din nuværende indholdsrating indstilling. + </notification> + <notification name="LandBuyAccessBlocked_Change"> + Du kan ikke købe dette land på grund af din valgte inholdsrating. + +Du kan vælge 'Indstillinger' for at hæve din indholdsrating nu og dermed få adgang. Du vil så få mulighed for at søge og få adgang til områder med indhold af typen [REGIONMATURITY]. Hvis du senere ønsker at skifte tilbage, kan du skifte tilbage i 'Indstillinger'. + <usetemplate + name="okcancelignore" + yestext="Ændre præferencer" + notext="Luk" + ignoretext="Når land ikke kan købes på grund af indholdsrating"/> + </notification> + <notification name="UnableToLoadNotecardAsset"> + Kunne ikke hente notecard indhold. + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="SetClassifiedMature"> + Indeholder denne annonce 'Mature' indhold? + <usetemplate + canceltext="Annullér" + name="yesnocancelbuttons" + notext="Nej" + yestext="Ja"/> + </notification> + <notification name="SetGroupMature"> + Indeholder denne gruppe 'Mature' indhold? + <usetemplate + canceltext="Annullér" + name="yesnocancelbuttons" + notext="Nej" + yestext="Ja"/> + </notification> + <notification label="Indholdsrating" name="HelpRegionMaturity"> + Sætter indholdsrating for regionen, som den vises øverst på menu-bjælken i beboernes klient, og i tooltips på verdenskortet når cursoren placeres over denne region. Denne indstilling har også betydning for adgangen til regionen og for søgeresultater. Andre beboere må kun få adgang til regionen eller se regionen i søgeresultater hvis de har valgt samme eller højere indholdsrating i deres opsætning. - <notification - - name="HelpReportAbuseEmailLL" - > -Brug dette værktøj for at rapportere brud på de almindelige bestemmelser og fællesskabs Standarder. Se: +Det kan tage noget tid inden en ændring af indholdsrating er synligt på kortet. + </notification> + <notification name="HelpReportAbuseEmailLL"> + Brug dette værktøj for at rapportere brud på de almindelige bestemmelser og fællesskabs Standarder. Se: http://secondlife.com/corporate/tos.php http://secondlife.com/corporate/cs.php @@ -496,13 +512,9 @@ http://secondlife.com/corporate/cs.php Alle rapporterede brud på almindelige bestemmelser og fællesskabs Standarder bliver undersøgt og løst. Du kan følge løsningen på din anmeldselse på: http://secondlife.com/support/incidentreport.php - </notification> - - <notification - - name="HelpReportAbuseContainsCopyright" - > -Dear Resident, + </notification> + <notification name="HelpReportAbuseContainsCopyright"> + Dear Resident, Du ser ud til at være ved at rapportere noget vedr. krænkelse af intellektuelle ejendomsrettigheder. Sørg for, at du rapporterer dette korrekt: @@ -515,1268 +527,659 @@ Hvis du stadig ønsker at fortsætte med misbrugs processen, luk da venligst det Mange tak, Linden Lab - </notification> - - <notification - - name="ConfirmClearCookies" - > -Er du sikker på du vil slette alle cookies? - </notification> - - <notification - - name="ChatterBoxSessionStartError" - > -Ikke i stand til at start chat med [RECIPIENT]. + </notification> + <notification name="ConfirmClearCookies"> + Er du sikker på du vil slette alle cookies? + </notification> + <notification name="NewSkyPreset"> + <form name="form"> + <input name="message" type="text"> + Ny forudindstilling + </input> + <button name="OK" text="OK"/> + <button name="Cancel" text="Annullér"/> + </form> + </notification> + <notification name="NewWaterPreset"> + <form name="form"> + <input name="message" type="text"> + Ny forudindstilling + </input> + <button name="OK" text="OK"/> + <button name="Cancel" text="Annullér"/> + </form> + </notification> + <notification name="ChatterBoxSessionStartError"> + Ikke i stand til at start chat med [RECIPIENT]. [REASON] - <usetemplate - name="okbutton" - yestext="OK"/> - </notification> - - <notification - - name="Cannot_Purchase_an_Attachment" - > -Ting kan ikke købes imens de er en del af tilbehør. - </notification> - - <notification - - name="NewWaterPreset" - > - <form name="form"> - <input name="message" type="text"> -Ny forudindstilling - </input> - <button - - - name="OK" - text="OK"/> - <button - - name="Cancel" - text="Annullér"/> - </form> - </notification> - - <notification - - name="NewSkyPreset" - > - <form name="form"> - <input name="message" type="text"> -Ny forudindstilling - </input> - <button - - - name="OK" - text="OK"/> - <button - - name="Cancel" - text="Annullér"/> - </form> - </notification> - - <notification - - name="OfferTeleport" - > - <form name="form"> - <input name="message" type="text"> -Mød mig i [REGION] - </input> - <button - - - name="OK" - text="OK"/> - <button - - name="Cancel" - text="Annullér"/> - </form> - </notification> - - <notification - - name="SystemMessageTip" - > -[MESSAGE] - </notification> - - <notification - - name="Cancelled" - > -Annulléret - </notification> - - <notification - - name="CancelledSit" - > -Annulléret sid - </notification> - - <notification - - name="CancelledAttach" - > -Annulléreret vedhæft - </notification> - - <notification - - name="ReplacedMissingWearable" - > -Erstattet manglende tøj/kropsdele med standard. - </notification> - - <notification - - name="FriendOnline" - > -[FIRST] [LAST] er Online - </notification> - - <notification - - name="FriendOffline" - > -[FIRST] [LAST] er Offline - </notification> - - <notification - - name="AddSelfFriend" - > -Du kan ikke tilføje dig selv som ven. - </notification> - - <notification - - name="UploadingAuctionSnapshot" - > -Uploader billeder fra verdenen og www... + <usetemplate name="okbutton" yestext="OK"/> + </notification> + <notification name="Cannot_Purchase_an_Attachment"> + Ting kan ikke købes imens de er en del af tilbehør. + </notification> + <notification name="SystemMessageTip"> + [MESSAGE] + </notification> + <notification name="Cancelled"> + Annulléret + </notification> + <notification name="CancelledSit"> + Annulléret sid + </notification> + <notification name="CancelledAttach"> + Annulléreret vedhæft + </notification> + <notification name="ReplacedMissingWearable"> + Erstattet manglende tøj/kropsdele med standard. + </notification> + <notification name="FriendOnline"> + [FIRST] [LAST] er Online + </notification> + <notification name="FriendOffline"> + [FIRST] [LAST] er Offline + </notification> + <notification name="AddSelfFriend"> + Du kan ikke tilføje dig selv som ven. + </notification> + <notification name="UploadingAuctionSnapshot"> + Uploader billeder fra verdenen og www... (Tager omkring 5 minutter.) - </notification> - - <notification - - name="UploadPayment" - > -Du betalte L$[AMOUNT] for at uploade. - </notification> - - <notification - - name="UploadWebSnapshotDone" - > -Billeder fra www er uploadet. - </notification> - - <notification - - name="UploadSnapshotDone" - > -Billeder fra verdenen er uploadet - </notification> - - <notification - - name="TerrainDownloaded" - > -Terrain.raw downloadet - </notification> - - <notification - - name="GestureMissing" - > -Gestus [NAME] mangler i databasen. - </notification> - - <notification - - name="UnableToLoadGesture" - > -Ikke muligt at indlæse gestus [NAME]. + </notification> + <notification name="UploadPayment"> + Du betalte L$[AMOUNT] for at uploade. + </notification> + <notification name="UploadWebSnapshotDone"> + Billeder fra www er uploadet. + </notification> + <notification name="UploadSnapshotDone"> + Billeder fra verdenen er uploadet + </notification> + <notification name="TerrainDownloaded"> + Terrain.raw downloadet + </notification> + <notification name="GestureMissing"> + Gestus [NAME] mangler i databasen. + </notification> + <notification name="UnableToLoadGesture"> + Ikke muligt at indlæse gestus [NAME]. Prøv venligst igen. - </notification> - - <notification - - name="LandmarkMissing" - > -Landmærke mangler i databasen. - </notification> - - <notification - - name="UnableToLoadLandmark" - > -Ikke muligt at indlæse landmærke. Prøv venligst igen. - </notification> - - <notification - - name="CapsKeyOn" - > -Du har slået store bogstaver til. + </notification> + <notification name="LandmarkMissing"> + Landmærke mangler i databasen. + </notification> + <notification name="UnableToLoadLandmark"> + Ikke muligt at indlæse landmærke. Prøv venligst igen. + </notification> + <notification name="CapsKeyOn"> + Du har slået store bogstaver til. Da det vil have betydning når du indtaster kodeordet, vil du højest sandsynlig slå dem fra. - </notification> - - <notification - - name="NotecardMissing" - > -Note mangler i databasen. - </notification> - - <notification - - name="NotecardNoPermissions" - > -Utilstrækkelige tilladelser til at se note. - </notification> - - <notification - - name="RezItemNoPermissions" - > -Utilstrækkelige tilladelser til at danne genstanden. - </notification> - - <notification - - name="UnableToLoadNotecard" - > -Ikke muligt at indlæse note. + </notification> + <notification name="NotecardMissing"> + Note mangler i databasen. + </notification> + <notification name="NotecardNoPermissions"> + Utilstrækkelige tilladelser til at se note. + </notification> + <notification name="RezItemNoPermissions"> + Utilstrækkelige tilladelser til at danne genstanden. + </notification> + <notification name="UnableToLoadNotecard"> + Ikke muligt at indlæse note. Prøv venligst igen. - </notification> - - <notification - - name="ScriptMissing" - > -Script mangler i databasen. - </notification> - - <notification - - name="ScriptNoPermissions" - > -Utilstrækkelige tilladelser til at se script. - </notification> - - <notification - - name="UnableToLoadScript" - > -Ikke muligt at indlæse script. Prøv venligst igen. - </notification> - - <notification - - name="IncompleteInventory" - > -Det komplette indhold, du tilbyder, er ikke endnu tilgængelig lokalt. Prøv venligst at tilbyde tingene igen om lidt. - </notification> - - <notification - - name="CannotModifyProtectedCategories" - > -Du kan ikke ændre beskyttede kategorier. - </notification> - - <notification - - name="CannotRemoveProtectedCategories" - > -Du kan ikke fjerne beskyttede kategorier. - </notification> - - <notification - - name="OfferedCard" - > -Du har tilbudt et visitkort til [FIRST] [LAST] - </notification> - - <notification - - name="OfferedFriendship" - > -Du har tilbudt venskab til [FIRST] [LAST] - </notification> - - <notification - - name="UnableToBuyWhileDownloading" - > -Ikke muligt at købe, imens genstandens data hentes. + </notification> + <notification name="ScriptMissing"> + Script mangler i databasen. + </notification> + <notification name="ScriptNoPermissions"> + Utilstrækkelige tilladelser til at se script. + </notification> + <notification name="UnableToLoadScript"> + Ikke muligt at indlæse script. Prøv venligst igen. + </notification> + <notification name="IncompleteInventory"> + Det komplette indhold, du tilbyder, er ikke endnu tilgængelig lokalt. Prøv venligst at tilbyde tingene igen om lidt. + </notification> + <notification name="CannotModifyProtectedCategories"> + Du kan ikke ændre beskyttede kategorier. + </notification> + <notification name="CannotRemoveProtectedCategories"> + Du kan ikke fjerne beskyttede kategorier. + </notification> + <notification name="OfferedCard"> + Du har tilbudt et visitkort til [FIRST] [LAST] + </notification> + <notification name="UnableToBuyWhileDownloading"> + Ikke muligt at købe, imens genstandens data hentes. Prøv venligst igen. - </notification> - - <notification - - name="UnableToLinkWhileDownloading" - > -Ikke muligt at lænke imens genstandens data hentes. + </notification> + <notification name="UnableToLinkWhileDownloading"> + Ikke muligt at lænke imens genstandens data hentes. Prøv venligst igen. - </notification> - - <notification - - name="CannotBuyObjectsFromDifferentOwners" - > -Kan ikke købe genstande fra forskellige ejere på samme tid. + </notification> + <notification name="CannotBuyObjectsFromDifferentOwners"> + Kan ikke købe genstande fra forskellige ejere på samme tid. Prøv venligst at vælge en enkelt genstand. - </notification> - - <notification - - name="ObjectNotForSale" - > -Genstanden ser ikke ud til at være til salg. - </notification> - - <notification - - name="EnteringGodMode" - > -Starter gud-tilstand, niveau [LEVEL] - </notification> - - <notification - - name="LeavingGodMode" - > -Stopper gud-tilstand, niveau [LEVEL] - </notification> - - <notification - - name="CopyFailed" - > -Kopiering lykkedes ikke fordi du ikke har nok tilladelser til at kopiere - </notification> - - <notification - - name="InventoryAccepted" - > -[NAME] accepterede det du tilbød fra din beholdning. - </notification> - - <notification - - name="InventoryDeclined" - > -[NAME] afviste det du tilbød fra din beholdning. - </notification> - - <notification - - name="ObjectMessage" - > -[NAME]: [MESSAGE] - </notification> - - <notification - - name="CallingCardAccepted" - > -Dit visitkort blev accepteret. - </notification> - - <notification - - name="CallingCardDeclined" - > -Dit visitkort blev afvist. - </notification> - - <notification - - name="TeleportToLandmark" - > -Nu hvor du er nået frem til hovedlandet, kan du teleportere til steder som '[NAME]' ved at klikke på Beholdning-knappen i nederste højre side af skærmen hvorefter du vælger Landmærke-mappen. + </notification> + <notification name="ObjectNotForSale"> + Genstanden ser ikke ud til at være til salg. + </notification> + <notification name="EnteringGodMode"> + Starter gud-tilstand, niveau [LEVEL] + </notification> + <notification name="LeavingGodMode"> + Stopper gud-tilstand, niveau [LEVEL] + </notification> + <notification name="CopyFailed"> + Kopiering lykkedes ikke fordi du ikke har nok tilladelser til at kopiere + </notification> + <notification name="InventoryAccepted"> + [NAME] accepterede det du tilbød fra din beholdning. + </notification> + <notification name="InventoryDeclined"> + [NAME] afviste det du tilbød fra din beholdning. + </notification> + <notification name="ObjectMessage"> + [NAME]: [MESSAGE] + </notification> + <notification name="CallingCardAccepted"> + Dit visitkort blev accepteret. + </notification> + <notification name="CallingCardDeclined"> + Dit visitkort blev afvist. + </notification> + <notification name="TeleportToLandmark"> + Nu hvor du er nået frem til hovedlandet, kan du teleportere til steder som '[NAME]' ved at klikke på Beholdning-knappen i nederste højre side af skærmen hvorefter du vælger Landmærke-mappen. Dobbeltklik på landmærket og klik på Teleportér, for at rejse derhen. - </notification> - - <notification - - name="TeleportToPerson" - > -Nu hvor du har nået frem til hovedlandet, kan du kontakte indbyggere som '[NAME]' ved at klikke på Beholdning-knappen i nederste højre side af skærmen, hvorefter du vælger Visitkort-mappen. + </notification> + <notification name="TeleportToPerson"> + Nu hvor du har nået frem til hovedlandet, kan du kontakte indbyggere som '[NAME]' ved at klikke på Beholdning-knappen i nederste højre side af skærmen, hvorefter du vælger Visitkort-mappen. Dobbeltklik på kortet, klik på IM og skriv beskeden. - </notification> - - <notification - - name="CantSelectLandFromMultipleRegions" - > -Kan ikke vælge land på tværs af grænser. + </notification> + <notification name="CantSelectLandFromMultipleRegions"> + Kan ikke vælge land på tværs af grænser. Prøv at vælge mindre stykker land. - </notification> - - <notification - - name="GroupVote" - > -[NAME] har forslået at stemme for: + </notification> + <notification name="SearchWordBanned"> + Visse ord er fjernet fra din søge-sætning på grund af at disse strider mod de generelle 'Community Standards'. + </notification> + <notification name="NoContentToSearch"> + Vælg venligst mindst en indholdstype for at søge (PG, Mature, or Adult). + </notification> + <notification name="GroupVote"> + [NAME] har forslået at stemme for: [MESSAGE] - <form name="form"> - <button - - name="VoteNow" - text="Stem nu"/> - <button - - name="Later" - text="Senere"/> - </form> - </notification> - - <notification - - name="SystemMessage" - > -[MESSAGE] - </notification> - - <notification - - name="EventNotification" - > -Besked om begivenhed: + <form name="form"> + <button name="VoteNow" text="Stem nu"/> + <button name="Later" text="Senere"/> + </form> + </notification> + <notification name="SystemMessage"> + [MESSAGE] + </notification> + <notification name="EventNotification"> + Besked om begivenhed: [NAME] [DATE] - <form name="form"> - <button - - name="Teleport" - text="Teleportér"/> - <button - - name="Description" - text="Beskrivelse"/> - <button - - name="Cancel" - text="Annullér"/> - </form> - </notification> - - <notification - - name="TransferObjectsHighlighted" - > -Alle genstande på denne grund, som vil blive overført til køberen af denne grund, er nu oplyst. + <form name="form"> + <button name="Teleport" text="Teleportér"/> + <button name="Description" text="Beskrivelse"/> + <button name="Cancel" text="Annullér"/> + </form> + </notification> + <notification name="TransferObjectsHighlighted"> + Alle genstande på denne grund, som vil blive overført til køberen af denne grund, er nu oplyst. * Træer og græs, der vil blive overført, er ikke fremhævet. - <form name="form"> - <button - - name="Done" - text="Færdig"/> - </form> - </notification> - - <notification - - name="DeactivatedGesturesTrigger" - > -Deaktiverede bevægelser med samme udløser: [NAMES] - </notification> - - <notification - - name="NoQuickTime" - > -Apple's QuickTime ser ikke ud til at være installeret på computeren. + <form name="form"> + <button name="Done" text="Færdig"/> + </form> + </notification> + <notification name="DeactivatedGesturesTrigger"> + Deaktiverede bevægelser med samme udløser: [NAMES] + </notification> + <notification name="NoQuickTime"> + Apple's QuickTime ser ikke ud til at være installeret på computeren. Hvis du vil se live transmitteret medie på grunde, der understøtter det, skal du gå ind på QuickTime-siden (http://www.apple.dk/quicktime) og installere QuickTime afspilleren. - </notification> - - <notification - - name="OwnedObjectsReturned" - > -De genstande du ejer på det valgte stykke land er blevet returneret til din beholdning. - </notification> - - <notification - - name="OtherObjectsReturned" - > -Genstandene på det valgte stykke land der er ejet af [FIRST] [LAST] er blevet returneret til hans eller hendes beholdning. - </notification> - - <notification - - name="OtherObjectsReturned2" - > -Genstandene på det valgte stykke land der er ejet af beboeren '[NAME]' er blevet returneret til deres ejere. - </notification> - - <notification - - name="GroupObjectsReturned" - > -Genstandene på det valgte stykke land, delt med gruppen [GROUPNAME], er blevet returneret til deres ejeres beholdninger. + </notification> + <notification name="OwnedObjectsReturned"> + De genstande du ejer på det valgte stykke land er blevet returneret til din beholdning. + </notification> + <notification name="OtherObjectsReturned"> + Genstandene på det valgte stykke land der er ejet af [FIRST] [LAST] er blevet returneret til hans eller hendes beholdning. + </notification> + <notification name="OtherObjectsReturned2"> + Genstandene på det valgte stykke land der er ejet af beboeren '[NAME]' er blevet returneret til deres ejere. + </notification> + <notification name="GroupObjectsReturned"> + Genstandene på det valgte stykke land, delt med gruppen [GROUPNAME], er blevet returneret til deres ejeres beholdninger. Genstande, som er dedikerede og som kan overføres, er blevet returneret til deres forrige ejere. Genstande, der ikke kan overføres og som er dedikeret til gruppen, er blevet slettet. - </notification> - - <notification - - name="UnOwnedObjectsReturned" - > -Genstandene på det valgte stykke land, der IKKE er ejet af dig, er blevet returneret til deres ejere. - </notification> - - <notification - - name="NotSafe" - > -Dette land har sat skade til ('ikke sikker'). + </notification> + <notification name="UnOwnedObjectsReturned"> + Genstandene på det valgte stykke land, der IKKE er ejet af dig, er blevet returneret til deres ejere. + </notification> + <notification name="NotSafe"> + Dette land har sat skade til ('ikke sikker'). Du kan komme til skade her. Hvis du dør, vil du blive teleporteret til din hjem-lokalitet. - </notification> - - <notification - - name="NoFly" - > -Dette land har slået flyvning fra ('ingen flyvning'). + </notification> + <notification name="NoFly"> + Dette land har slået flyvning fra ('ingen flyvning'). Du kan ikke flyve her. - </notification> - - <notification - - name="PushRestricted" - > -Dette land giver ikke mulighed for at 'skubbe' andre, med mindre du ejer landet. - </notification> - - <notification - - name="NoVoice" - > -Dette land har ikke mulighed for at bruge stemme. - </notification> - - <notification - - name="NoBuild" - > -Dette land giver ikke mulighed for at bygge ('byggeri forbudt'). + </notification> + <notification name="PushRestricted"> + Dette land giver ikke mulighed for at 'skubbe' andre, med mindre du ejer landet. + </notification> + <notification name="NoVoice"> + Dette land har ikke mulighed for at bruge stemme. + </notification> + <notification name="NoBuild"> + Dette land giver ikke mulighed for at bygge ('byggeri forbudt'). Du kan ikke skabe genstande her. - </notification> - - <notification - - name="ScriptsStopped" - > -En administrator har midlertidig stoppet scripts i denne region. - </notification> - - <notification - - name="ScriptsNotRunning" - > -Denne region kører ikke nogen scripts. - </notification> - - <notification - - name="NoOutsideScripts" - > -Dette land har eksterne scripts slået fra + </notification> + <notification name="ScriptsStopped"> + En administrator har midlertidig stoppet scripts i denne region. + </notification> + <notification name="ScriptsNotRunning"> + Denne region kører ikke nogen scripts. + </notification> + <notification name="NoOutsideScripts"> + Dette land har eksterne scripts slået fra ('ingen eksterne scripts'). Ingen scripts vil køre på nær dem, som tilhører ejeren af landet. - </notification> - - <notification - - name="ClaimPublicLand" - > -Du kan kun gøre krav på offentlig land i den region, du befinder dig i. - </notification> - - <notification - - name="ObjectGiveItem" - > -En genstand med navnet [OBJECTFROMNAME], ejet af [FIRST] [LAST], har givet dig en/et [OBJECTTYPE] med navnet [OBJECTNAME]. - <form name="form"> - <button - - name="Keep" - text="Behold"/> - <button - - name="Discard" - text="Smid væk"/> - <button - - name="Mute" - text="Blokér"/> - </form> - </notification> - - <notification - - name="ObjectGiveItemUnknownUser" - > -En genstand med navnet [OBJECTFROMNAME], ejet af (en ukendt bruger), har givet dig en/et [OBJECTTYPE] med navnet [OBJECTNAME]. - <form name="form"> - <button - - name="Keep" - text="Behold"/> - <button - - name="Discard" - text="Smid væk"/> - <button - - name="Mute" - text="Blokér"/> - </form> - </notification> - - <notification - - name="UserGiveItem" - > -[NAME] har givet dig en/et [OBJECTTYPE] med navnet '[OBJECTNAME]'. - <form name="form"> - <button - - name="Keep" - text="Behold"/> - <button - - name="Discard" - text="Smid væk"/> - <button - - name="Mute" - text="Blokér"/> - </form> - </notification> - - <notification - - name="GodMessage" - > -[NAME] + </notification> + <notification name="ClaimPublicLand"> + Du kan kun gøre krav på offentlig land i den region, du befinder dig i. + </notification> + <notification name="RegionTPAccessBlocked"> + Du har ikke adgang til denne region på grund af din valgte indholdsrating. Dette kan skyldes manglende validering af din alder eller at du ikke benytter den nyeste Second Life klient. + +Gå venligst til 'Knowledge Base' for yderligere detaljer om adgang til områder med denne indholdsrating. + </notification> + <notification name="URBannedFromRegion"> + Du er blokeret i denne region. + </notification> + <notification name="NoTeenGridAccess"> + Du kan ikke tilslutte dig denne 'Teen' region. + </notification> + <notification name="NoHelpIslandTP"> + Du kan ikke teleportere tilbage til Help Island. +Gå til 'Help Island Puclic' for at prøve tutorial igen. + </notification> + <notification name="ImproperPaymentStatus"> + Du har ikke de rette betalingsoplysninger til at komme ind i denne region. + </notification> + <notification name="MustGetAgeRegion"> + Du skal være aldersgodkendt for at komme ind i denne region. + </notification> + <notification name="MustGetAgeParcel"> + Du skal være aldersgodkendt for at komme ind på denne parcel. + </notification> + <notification name="NoDestRegion"> + Destinations region ikke fundet. + </notification> + <notification name="NotAllowedInDest"> + Du har ikke adgang til denne destination. + </notification> + <notification name="RegionParcelBan"> + Kan ikke skifte til ny region via en blokeret parcel. Prøv en anden vej ind. + </notification> + <notification name="TelehubRedirect"> + Du er blevet omdirigeret til en telehub. + </notification> + <notification name="CouldntTPCloser"> + Kunne ikke teleportere nærmere til destination. + </notification> + <notification name="TPCancelled"> + Teleport afbrudt. + </notification> + <notification name="FullRegionTryAgain"> + Den region du prøver at komme ind i er fuld for øjeblikket. +Prøv igen om lidt. + </notification> + <notification name="GeneralFailure"> + Generel fejl. + </notification> + <notification name="RoutedWrongRegion"> + Du blev sendt til en forkert region. Prøv igen. + </notification> + <notification name="NoValidAgentID"> + Ikke en gyldig agent ID. + </notification> + <notification name="NoValidSession"> + Ikke noget gyldig sessions-ID + </notification> + <notification name="NoValidCircuit"> + Ingen gyldig kode for kredsløb. + </notification> + <notification name="NoValidTimestamp"> + Ikke et gyldigt klokkeslæt. + </notification> + <notification name="NoPendingConnection"> + Kunne ikke skabe fast forbindelse. + </notification> + <notification name="InternalUsherError"> + Der opstod en intern fejl ved teleportering til din teleport destination.. Der kan være generelle problemer med Second Life lige nu. + </notification> + <notification name="NoGoodTPDestination"> + Kunne ikke finde et egnet teleport sted i denne region. + </notification> + <notification name="InternalErrorRegionResolver"> + Der opstod en intern fejl ved beregning af globale koordinater for din teleport forespørgsel. Der kan være generelle problemer med Second Life lige nu. + </notification> + <notification name="NoValidLanding"> + Kunne ikke finde et gyldigt landingspunkt. + </notification> + <notification name="NoValidParcel"> + No valid parcel could be found. + </notification> + <notification name="ObjectGiveItem"> + En genstand med navnet [OBJECTFROMNAME], ejet af [FIRST] [LAST], har givet dig en/et [OBJECTTYPE] med navnet [OBJECTNAME]. + <form name="form"> + <button name="Keep" text="Behold"/> + <button name="Discard" text="Smid væk"/> + <button name="Mute" text="Blokér"/> + </form> + </notification> + <notification name="ObjectGiveItemUnknownUser"> + En genstand med navnet [OBJECTFROMNAME], ejet af (en ukendt bruger), har givet dig en/et [OBJECTTYPE] med navnet [OBJECTNAME]. + <form name="form"> + <button name="Keep" text="Behold"/> + <button name="Discard" text="Smid væk"/> + <button name="Mute" text="Blokér"/> + </form> + </notification> + <notification name="UserGiveItem"> + [NAME] har givet dig en/et [OBJECTTYPE] med navnet '[OBJECTNAME]'. + <form name="form"> + <button name="Keep" text="Behold"/> + <button name="Discard" text="Smid væk"/> + <button name="Mute" text="Blokér"/> + </form> + </notification> + <notification name="GodMessage"> + [NAME] [MESSAGE] - </notification> + </notification> + <notification name="JoinGroup"> + [MESSAGE] + <form name="form"> + <button name="Join" text="Indmeld"/> + <button name="Decline" text="Afvis"/> + <button name="Info" text="Information"/> + </form> + </notification> + <notification name="TeleportOffered"> + [NAME] har tilbudt at teleportere dig til hans eller hendes lokalitet: - <notification - - name="JoinGroup" - > -[MESSAGE] - <form name="form"> - <button - - name="Join" - text="Indmeld"/> - <button - - name="Decline" - text="Afvis"/> - <button - - name="Info" - text="Information"/> - </form> - </notification> - - <notification - - name="TeleportOffered" - > -[NAME] har tilbudt at teleportere dig til hans eller hendes lokalitet: - -[MESSAGE] - <form name="form"> - <button - - name="Teleport" - text="Teleportér"/> - <button - - name="Cancel" - text="Annullér"/> - </form> - </notification> - - <notification - - name="GotoURL" - > [MESSAGE] + <form name="form"> + <button name="Teleport" text="Teleportér"/> + <button name="Cancel" text="Annullér"/> + </form> + </notification> + <notification name="GotoURL"> + [MESSAGE] [URL] - <form name="form"> - <button - - name="Later" - text="Senere"/> - <button - - name="GoNow..." - text="Gå nu..."/> - </form> - </notification> - - <notification - - name="OfferFriendship" - > -[NAME] tilbyder venskab. + <form name="form"> + <button name="Later" text="Senere"/> + <button name="GoNow..." text="Gå nu..."/> + </form> + </notification> + <notification name="OfferFriendship"> + [NAME] tilbyder venskab. Som standard vil du kunne se andres onlinestatus. - <form name="form"> - <button - - name="Accept" - text="Acceptér"/> - <button - - name="Decline" - text="Afvis"/> - </form> - </notification> - - <notification - - name="FriendshipAccepted" - > -[NAME] accepterede dit tilbud om venskab. - </notification> - - <notification - - name="FriendshipDeclined" - > -[NAME] afviste dit tilbud om venskab. - </notification> - - <notification - - name="OfferCallingCard" - > -[FIRST] [LAST] tilbyder vedkommendes visitkort. + <form name="form"> + <button name="Accept" text="Acceptér"/> + <button name="Decline" text="Afvis"/> + </form> + </notification> + <notification name="FriendshipAccepted"> + [NAME] accepterede dit tilbud om venskab. + </notification> + <notification name="FriendshipDeclined"> + [NAME] afviste dit tilbud om venskab. + </notification> + <notification name="OfferCallingCard"> + [FIRST] [LAST] tilbyder vedkommendes visitkort. Dette vil tilføje et bogmærke i din beholdning, så du hurtigt kan sende en personlig besked (IM) til denne beboer. - <form name="form"> - <button - - name="Accept" - text="Acceptér"/> - <button - - name="Decline" - text="Afvis"/> - </form> - </notification> - - <notification - - name="RegionRestartMinutes" - - - > -Regionen genstarter om [MINUTES] minutter. + <form name="form"> + <button name="Accept" text="Acceptér"/> + <button name="Decline" text="Afvis"/> + </form> + </notification> + <notification name="RegionRestartMinutes"> + Regionen genstarter om [MINUTES] minutter. Hvis du bliver i denne region, vil du blive logget af. - </notification> - - <notification - - name="RegionRestartSeconds" - - - > -Regionen genstarter om [SECONDS] sekunder. + </notification> + <notification name="RegionRestartSeconds"> + Regionen genstarter om [SECONDS] sekunder. Hvis du bliver i denne region, vil du blive logget af. - </notification> - - <notification - - name="LoadWebPage" - > -Indlæs internetside [URL]? + </notification> + <notification name="LoadWebPage"> + Indlæs internetside [URL]? [MESSAGE] Fra genstand: [OBJECTNAME], ejer: [NAME]? - <form name="form"> - <button - - name="Gotopage" - text="Gå til side"/> - <button - - name="Cancel" - text="Afbryd"/> - </form> - </notification> - - <notification - - name="FailedToFindWearableUnnamed" - > -Det lykkedes ikke at finde [TYPE] i databasen. - </notification> - - <notification - - name="FailedToFindWearable" - > -Det lykkedes ikke at finde [TYPE] med navnet [DESC] i databasen. - </notification> - - <notification - - name="ScriptQuestion" - > -'[OBJECTNAME]', en genstand, ejet af '[NAME]', vil gerne: + <form name="form"> + <button name="Gotopage" text="Gå til side"/> + <button name="Cancel" text="Afbryd"/> + </form> + </notification> + <notification name="FailedToFindWearableUnnamed"> + Det lykkedes ikke at finde [TYPE] i databasen. + </notification> + <notification name="FailedToFindWearable"> + Det lykkedes ikke at finde [TYPE] med navnet [DESC] i databasen. + </notification> + <notification name="InvalidWearable"> + Den genstand du prøver at tage på benytter funktioner som din klient ikke kan forstå. Opdatér din version af Second Life for at tage genstanden på. + </notification> + <notification name="ScriptQuestion"> + '[OBJECTNAME]', en genstand, ejet af '[NAME]', vil gerne: [QUESTIONS] Er det iorden? - <form name="form"> - <button - - name="Yes" - text="Ja"/> - <button - - name="No" - text="Nej"/> - <button - - name="Mute" - text="Blokér"/> - </form> - </notification> - - <notification - - name="ScriptQuestionCaution" - > -'[OBJECTNAME]', en genstand, ejet af '[NAME]', vil gerne: + <form name="form"> + <button name="Yes" text="Ja"/> + <button name="No" text="Nej"/> + <button name="Mute" text="Blokér"/> + </form> + </notification> + <notification name="ScriptQuestionCaution"> + '[OBJECTNAME]', en genstand, ejet af '[NAME]', vil gerne: [QUESTIONS] Hvis du ikke har tillid til denne genstand og dens skaber, bør du afvise denne forespørgsel. For yderligere information klik på Detaljer-knappen. Imødekom denne forespørgsel? - <form name="form"> - <button - - name="Grant" - text="Imødekom"/> - <button - - name="Deny" - text="Afvis"/> - <button - - name="Details" - text="Detaljer..."/> - </form> - </notification> - - <notification - - name="ScriptDialog" - > -[FIRST] [LAST]'s '[TITLE]' + <form name="form"> + <button name="Grant" text="Imødekom"/> + <button name="Deny" text="Afvis"/> + <button name="Details" text="Detaljer..."/> + </form> + </notification> + <notification name="ScriptDialog"> + [FIRST] [LAST]'s '[TITLE]' [MESSAGE] - <form name="form"> - <button - - name="Ignore" - text="Ignorér"/> - </form> - </notification> - - <notification - - name="ScriptDialogGroup" - > -[GROUPNAME]'s '[TITLE]' + <form name="form"> + <button name="Ignore" text="Ignorér"/> + </form> + </notification> + <notification name="ScriptDialogGroup"> + [GROUPNAME]'s '[TITLE]' [MESSAGE] - <form name="form"> - <button - - name="Ignore" - text="Ignorér"/> - </form> - </notification> - - <notification - - name="FirstBalanceIncrease" - > -Du har lige modtaget L$[AMOUNT]. + <form name="form"> + <button name="Ignore" text="Ignorér"/> + </form> + </notification> + <notification name="FirstBalanceIncrease"> + Du har lige modtaget L$[AMOUNT]. Genstande og andre brugere kan give dig L$. Din saldo er vist i øverste højre hjørne af skærmen. - </notification> - - <notification - - name="FirstBalanceDecrease" - > -Du har lige modtaget L$[AMOUNT]. + </notification> + <notification name="FirstBalanceDecrease"> + Du har lige modtaget L$[AMOUNT]. Din saldo er vist i øverste højre hjørne af skærmen. - </notification> - - <notification - - name="FirstSit" - > -Du sidder. + </notification> + <notification name="FirstSit"> + Du sidder. Brug piletasterne (eller AWSD) for at ændre hvilken vej du ser. Klik på 'Stå op'-knappen for at rejse dig op. - </notification> - - <notification - - name="FirstMap" - > -Klik og træk for at flytte kortvisningen. + </notification> + <notification name="FirstMap"> + Klik og træk for at flytte kortvisningen. Dobbelt-klik for at teleportere. Brug kontrollerne til højre for at finde ting og se forskellige baggrunde. - </notification> - - <notification - - name="FirstBuild" - > -Du kan bygge nye genstande i nogle områder af [SECOND_LIFE]. + </notification> + <notification name="FirstBuild"> + Du kan bygge nye genstande i nogle områder af [SECOND_LIFE]. Brug værktøjet øverst til venstre for at bygge, og prøv at holde Ctrl eller Alt nede for hurtigt at skifte imellem værktøjerne. Tryk Esc for at stoppe med at bygge. - </notification> - - <notification - - name="FirstLeftClickNoHit" - > -Venstre-klik interagerer med specielle genstande. + </notification> + <notification name="FirstLeftClickNoHit"> + Venstre-klik interagerer med specielle genstande. Hvis musemarkøren ændrer sig til en hånd, kan du interagere med genstanden. Højre-klik viser altid en menu med ting du kan gøre. - </notification> - - <notification - - name="FirstTeleport" - > -Du har lige teleporteret. + </notification> + <notification name="FirstTeleport"> + Du har lige teleporteret. Du er ved info-standen nærmest ved din destination. Din destination er markeret med en stor rød lyskegle. - </notification> - - <notification - - name="FirstOverrideKeys" - > -Dine bevælgelsestaster bliver nu håndteret af et objekt. + </notification> + <notification name="FirstOverrideKeys"> + Dine bevælgelsestaster bliver nu håndteret af et objekt. Brug piletasterne eller AWSD for at se, hvad de gør. Nogle genstande (som skydevåben) kræver at du går ind i musevisning for at bruge dem. Tryk på 'M' for at gåre det. - </notification> - - <notification - - name="FirstAppearance" - > -Du tilretter dit udseende. + </notification> + <notification name="FirstAppearance"> + Du tilretter dit udseende. For at rotere og zoome brug piletasterne. Når du er færdig, tryk på 'Gem alt' for at gemme dit udseende og lukke. Du kan rette dit udseende så tit du vil. - </notification> - - <notification - - name="FirstInventory" - > -Dette er din beholdning, der indeholder objekter, noter, tøj og andre ting du ejer. + </notification> + <notification name="FirstInventory"> + Dette er din beholdning, der indeholder objekter, noter, tøj og andre ting du ejer. * For at bære et objekt eller en mappe med tøj, træk den over på dig selv. * For at få et objekt frem i verdenen, træk den ud på jorden. * For at læse en note, dobbeltklik på den. - </notification> - - <notification - - name="FirstSandbox" - > -Dette er sandkasseområdet. + </notification> + <notification name="FirstSandbox"> + Dette er sandkasseområdet. Genstande, der er skabt her, vil blive slettet efter du har forladt området. Sandkasser renses jævnligt. Se venligst informationen øverst på skærmen, lige ved siden af områdenavnet. Sandkasseområder er ikke almindelige. De er mærket med skilte. - </notification> - - <notification - - name="FirstFlexible" - > -Denne genstand er fleksibel. + </notification> + <notification name="FirstFlexible"> + Denne genstand er fleksibel. Fleksible genstande er ikke fysiske og man kan gå igennem dem, indtil fleksibel-punktet ikke er afkrydset. - </notification> - - <notification - - name="FirstDebugMenus" - > -Du har sat avanceret menu til. + </notification> + <notification name="FirstDebugMenus"> + Du har sat avanceret menu til. Denne menu indeholder funktioner brugbare for udviklere, der udbedrer fejl i Second Life. -For at vise denne menu, skal man i Windows trykke Ctrl-Alt-D. På Mac tryk Cmd-Opt-Shift-D. - </notification> - - <notification - - name="FirstSculptedPrim" - > -Du retter en sculpted prim. +For at vise denne menu, skal man i Windows trykke Ctrl-Alt-D. På Mac tryk ⌘-Opt-Shift-D. + </notification> + <notification name="FirstSculptedPrim"> + Du retter en sculpted prim. Sculpted prims kræver et specielt tekstur for at specificere deres form. Du kan finde eksempler på sculptede teksturer i din beholdning. - </notification> - - <notification - - name="FirstMedia" - > -Du er begyndt at afspille medie. Medie kan sættes til automatisk af blive afspillet under Indstillinger, Lyd / Video. Vær opmærksom på, at der kan være en sikkerhedsrisiko for medie-steder, du ikke stoler på. - </notification> - - <notification - - name="MaxListSelectMessage" - > -Du må kun vælge op til [MAX_SELECT] genstande på denne liste. - </notification> - - <notification - - name="VoiceInviteP2P" - > -[NAME] inviterer dig til en stemme-chat. + </notification> + <notification name="FirstMedia"> + Du er begyndt at afspille medie. Medie kan sættes til automatisk af blive afspillet under Indstillinger, Lyd / Video. Vær opmærksom på, at der kan være en sikkerhedsrisiko for medie-steder, du ikke stoler på. + </notification> + <notification name="MaxListSelectMessage"> + Du må kun vælge op til [MAX_SELECT] genstande på denne liste. + </notification> + <notification name="VoiceInviteP2P"> + [NAME] inviterer dig til en stemme-chat. Klik for at acceptere at koble dig på samtalen eller Afvis for at afvise invitationen. Klik på Slå fra for at blokere denne opkalder. - <form name="form"> - <button - - name="Accept" - text="Acceptér"/> - <button - - name="Decline" - text="Afvis"/> - <button - - name="Mute" - text="Blokér"/> - </form> - </notification> - - <notification - - name="AutoUnmuteByIM" - > -[FIRST] [LAST] har fået en personlig besked (IM) og er automatisk ikke blokeret mere. - </notification> - - <notification - - name="AutoUnmuteByMoney" - > -[FIRST] [LAST] har fået penge og er automatisk ikke blokeret mere. - </notification> - - <notification - - name="AutoUnmuteByInventory" - > -[FIRST] [LAST] har fået tilbudt genstande og er automatisk ikke blokeret mere. - </notification> - - <notification - - name="VoiceInviteGroup" - > -[NAME] har tilsluttet sig stemme-chat med gruppen [GROUP]. + <form name="form"> + <button name="Accept" text="Acceptér"/> + <button name="Decline" text="Afvis"/> + <button name="Mute" text="Blokér"/> + </form> + </notification> + <notification name="AutoUnmuteByIM"> + [FIRST] [LAST] har fået en personlig besked (IM) og er automatisk ikke blokeret mere. + </notification> + <notification name="AutoUnmuteByMoney"> + [FIRST] [LAST] har fået penge og er automatisk ikke blokeret mere. + </notification> + <notification name="AutoUnmuteByInventory"> + [FIRST] [LAST] har fået tilbudt genstande og er automatisk ikke blokeret mere. + </notification> + <notification name="VoiceInviteGroup"> + [NAME] har tilsluttet sig stemme-chat med gruppen [GROUP]. Klik Acceptér for at slutte dig til samtalen eller Afvis for at afvise invitationen. Klik Slå fra for at blokere denne opkalder. - <form name="form"> - <button - - name="Accept" - text="Acceptér"/> - <button - - name="Decline" - text="Afvis"/> - <button - - name="Mute" - text="Blokér"/> - </form> - </notification> - - <notification - - name="VoiceInviteAdHoc" - > -[NAME] har tilsluttet sig stemme-chat med en konference-chat. + <form name="form"> + <button name="Accept" text="Acceptér"/> + <button name="Decline" text="Afvis"/> + <button name="Mute" text="Blokér"/> + </form> + </notification> + <notification name="VoiceInviteAdHoc"> + [NAME] har tilsluttet sig stemme-chat med en konference-chat. Klik Acceptér for at slutte dig til samtalen eller Afvis for at afvise invitationen. Klik Slå fra for at blokere denne opkalder. - <form name="form"> - <button - - name="Accept" - text="Acceptér"/> - <button - - name="Decline" - text="Afvis"/> - <button - - name="Mute" - text="Blokér"/> - </form> - </notification> - - <notification - - name="InviteAdHoc" - > -[NAME] inviterer dig til en konference-chat. + <form name="form"> + <button name="Accept" text="Acceptér"/> + <button name="Decline" text="Afvis"/> + <button name="Mute" text="Blokér"/> + </form> + </notification> + <notification name="InviteAdHoc"> + [NAME] inviterer dig til en konference-chat. Klik Acceptér for at slutte dig til samtalen eller Afvis for at afvise invitationen. Klik Slå fra for at blokere denne opkalder. - <form name="form"> - <button - - name="Accept" - text="Acceptér"/> - <button - - name="Decline" - text="Afvis"/> - <button - - name="Mute" - text="Blokeret"/> - </form> - </notification> - - <notification - - name="VoiceChannelFull" - > -Den stemme-chat, du prøver at tilslutte dig, [VOICE_CHANNEL_NAME], har nået maksiumum kapacitet. Prøv venligst igen senere. - </notification> - - <notification - - name="ProximalVoiceChannelFull" - > -Vi beklager. Dette område har nået sin maksimale kapacitet for stemme-chat. Prøv venligst at benytte stemme i et andet område. - </notification> - - <notification - - name="VoiceChannelDisconnected" - > -Du er blevet koblet af [VOICE_CHANNEL_NAME]. Du vil nu blive koblet op på en lokal stemme-chat. - </notification> - - <notification - - name="VoiceChannelDisconnectedP2P" - > -[VOICE_CHANNEL_NAME] har afsluttet opkaldet. Du vil nu blive koblet op på en lokal stemme-chat. - </notification> - - <notification - - name="P2PCallDeclined" - > -[VOICE_CHANNEL_NAME] har afvist dit opkald. Du vil nu blive koblet op på en lokal stemme-chat. - </notification> - - <notification - - name="P2PCallNoAnswer" - > -[VOICE_CHANNEL_NAME] har ikke mulighed for at besvare dit opkald. Du vil nu blive koblet op på en lokal chat. - </notification> - - <notification - - name="VoiceChannelJoinFailed" - > -Det lykkedes ikke at koble op til [VOICE_CHANNEL_NAME]. Prøv venligst igen senere. - </notification> - - <notification - - name="VoiceLoginRetry" - > -Vi laver en stemmekanal til dig. Det kan tage op til et minut. - </notification> - - <notification - - name="Cannot enter parcel: not a group member" - > -Du kan ikke komme ind på området. Du er ikke medlem af den nødvendige gruppe. - </notification> - - <notification - - name="Cannot enter parcel: banned" - > -Du kan ikke komme ind på området. Du er blevet udelukket. - </notification> - - <notification - - name="Cannot enter parcel: not on access list" - > -Du kan ikke komme ind på området. Du er ikke på adgangslisten. - </notification> - - <notification - - name="VoiceNotAllowed" - > -Du har ikke tilladelse til at tilslutte dig stemme-chat på [VOICE_CHANNEL_NAME]. - </notification> - - <notification - - name="VoiceCallGenericError" - > -En fejl er opstået under forsøget på at koble sig på stemme chatten [VOICE_CHANNEL_NAME]. Pråv venligst senere. - </notification> - - <notification - - name="ServerVersionChanged" - > -Det område, du er kommet ind på, kører en anden simulatorversion. Klik på denne besked for detaljer. - </notification> - - <notification - - name="UnableToOpenCommandURL" - > -Www-adressen, du har klikket på, kan ikke åbnes fra denne internetbrowser. - </notification> + <form name="form"> + <button name="Accept" text="Acceptér"/> + <button name="Decline" text="Afvis"/> + <button name="Mute" text="Blokeret"/> + </form> + </notification> + <notification name="VoiceChannelFull"> + Den stemme-chat, du prøver at tilslutte dig, [VOICE_CHANNEL_NAME], har nået maksiumum kapacitet. Prøv venligst igen senere. + </notification> + <notification name="ProximalVoiceChannelFull"> + Vi beklager. Dette område har nået sin maksimale kapacitet for stemme-chat. Prøv venligst at benytte stemme i et andet område. + </notification> + <notification name="VoiceChannelDisconnected"> + Du er blevet koblet af [VOICE_CHANNEL_NAME]. Du vil nu blive koblet op på en lokal stemme-chat. + </notification> + <notification name="VoiceChannelDisconnectedP2P"> + [VOICE_CHANNEL_NAME] har afsluttet opkaldet. Du vil nu blive koblet op på en lokal stemme-chat. + </notification> + <notification name="P2PCallDeclined"> + [VOICE_CHANNEL_NAME] har afvist dit opkald. Du vil nu blive koblet op på en lokal stemme-chat. + </notification> + <notification name="P2PCallNoAnswer"> + [VOICE_CHANNEL_NAME] har ikke mulighed for at besvare dit opkald. Du vil nu blive koblet op på en lokal chat. + </notification> + <notification name="VoiceChannelJoinFailed"> + Det lykkedes ikke at koble op til [VOICE_CHANNEL_NAME]. Prøv venligst igen senere. + </notification> + <notification name="VoiceLoginRetry"> + Vi laver en stemmekanal til dig. Det kan tage op til et minut. + </notification> + <notification name="Cannot enter parcel: not a group member"> + Du kan ikke komme ind på området. Du er ikke medlem af den nødvendige gruppe. + </notification> + <notification name="Cannot enter parcel: banned"> + Du kan ikke komme ind på området. Du er blevet udelukket. + </notification> + <notification name="Cannot enter parcel: not on access list"> + Du kan ikke komme ind på området. Du er ikke på adgangslisten. + </notification> + <notification name="VoiceNotAllowed"> + Du har ikke tilladelse til at tilslutte dig stemme-chat på [VOICE_CHANNEL_NAME]. + </notification> + <notification name="VoiceCallGenericError"> + En fejl er opstået under forsøget på at koble sig på stemme chatten [VOICE_CHANNEL_NAME]. Pråv venligst senere. + </notification> + <notification name="ServerVersionChanged"> + Det område, du er kommet ind på, kører en anden simulatorversion. Klik på denne besked for detaljer. + </notification> + <notification name="UnableToOpenCommandURL"> + Www-adressen, du har klikket på, kan ikke åbnes fra denne internetbrowser. + </notification> </notifications> - diff --git a/indra/newview/skins/default/xui/de/floater_world_map.xml b/indra/newview/skins/default/xui/de/floater_world_map.xml index 555b78b837..fecaf3eaff 100644 --- a/indra/newview/skins/default/xui/de/floater_world_map.xml +++ b/indra/newview/skins/default/xui/de/floater_world_map.xml @@ -52,6 +52,6 @@ <button label="Gesuchte Position" label_selected="Ziel anzeigen" name="Show Destination" tool_tip="Karte auf ausgewählte Position zentrieren"/> <button label="Löschen" label_selected="Löschen" name="Clear" tool_tip="Verfolgung abschalten"/> <button label="Meine Position" label_selected="Wo bin ich?" name="Show My Location" tool_tip="Karte auf Position Ihres Avatars zentrieren"/> - <button font="SansSerifSmall" label="SLURL in die Zwischenablage kopieren" name="copy_slurl" tool_tip="Kopiert die aktuelle Position als SLURL zur Verwendung im Web."/> + <button font="SansSerifSmall" label="SLurl in die Zwischenablage kopieren" name="copy_slurl" tool_tip="Kopiert die aktuelle Position als SLurl zur Verwendung im Web."/> <slider label="Zoom" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/de/notifications.xml b/indra/newview/skins/default/xui/de/notifications.xml index e920b2451f..06a432bd3b 100644 --- a/indra/newview/skins/default/xui/de/notifications.xml +++ b/indra/newview/skins/default/xui/de/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> Diese Option immer auswählen </global> + <global name="implicitclosebutton"> + Schließen + </global> <template name="okbutton"> <form> <button @@ -2072,12 +2075,12 @@ Möchten Sie den Bechäftigt-Modus verlassen, bevor Sie diese Transaktion abschl <usetemplate ignoretext="Beim Leeren von Inventar und Fundstückeordner-" name="okcancelignore" notext="Nein" yestext="Ja"/> </notification> <notification name="CopySLURL"> - Die folgende SLURL wurde in die Zwischenablage kopiert: + Die folgende SLurl wurde in die Zwischenablage kopiert: [SLURL] Veröffentlichen Sie sie auf einer Website, um anderen den Zugang zu diesem Ort zu erleichtern, oder testen Sie sie, indem Sie sie in die Adressleiste Ihres Browsers kopieren. <form name="form"> - <ignore name="ignore" text="Beim Kopieren einer SLURL in die Zwischenablage"/> + <ignore name="ignore" text="Beim Kopieren einer SLurl in die Zwischenablage"/> </form> </notification> <notification name="GraphicsPreferencesHelp"> @@ -2598,7 +2601,7 @@ Gehen Sie zu „Help Island Public“ und wiederholen sie das Tutorial. <notification name="ImproperPaymentStatus"> Die für den Zutritt zu dieser Region erforderlichen Zahlungsinformationen liegen nicht vor. </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> Sie müssen alterüberprüft sein, um diese Region betreten zu können. </notification> <notification name="MustGetAgeParcel"> diff --git a/indra/newview/skins/default/xui/en/favorites_bar_button.xml b/indra/newview/skins/default/xui/en/favorites_bar_button.xml index 4525df31b6..01d8054e9a 100644 --- a/indra/newview/skins/default/xui/en/favorites_bar_button.xml +++ b/indra/newview/skins/default/xui/en/favorites_bar_button.xml @@ -1,11 +1,9 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<!-- *NOTE: mantipov: to use the "use_ellipses" attribute label should be LEFT aligned -untill LLFontGL::render() is fixed to avoid this requirement--> <!-- *NOTE: mantipov: top & height should be synchronized with <favorites_bar> in the panel_navigation_bar.xml--> <!-- All buttons in the Favorites bar will be created from this one --> <button follows="left|bottom" - halign="left" + halign="center" height="23" image_disabled="transparent.j2c" image_disabled_selected="PushButton_Selected" diff --git a/indra/newview/skins/default/xui/en/floater_aaa.xml b/indra/newview/skins/default/xui/en/floater_aaa.xml index d2a0193763..bf8988ca45 100644 --- a/indra/newview/skins/default/xui/en/floater_aaa.xml +++ b/indra/newview/skins/default/xui/en/floater_aaa.xml @@ -4,7 +4,8 @@ layout="topleft" name="floater_aaa" save_rect="true" - title="About [SECOND_LIFE_VIEWER]" + can_resize="true" + title="About [APP_NAME]" width="470"> <text_editor follows="left|top|right|bottom" @@ -16,9 +17,6 @@ top="25" width="458" word_wrap="true"> - This is line 1a - - - This is line 4 +This is line 4 </text_editor> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_about.xml b/indra/newview/skins/default/xui/en/floater_about.xml index ece0cf737f..73d789ce89 100644 --- a/indra/newview/skins/default/xui/en/floater_about.xml +++ b/indra/newview/skins/default/xui/en/floater_about.xml @@ -4,7 +4,7 @@ layout="topleft" name="floater_about" save_rect="true" - title="About [SECOND_LIFE_VIEWER]" + title="About [APP_NAME]" width="470"> <floater.string name="you_are_at"> @@ -58,7 +58,11 @@ name="LLMozLibVersion"> LLMozLib Version: </floater.string> - <floater.string + <floater.string + name="LLQtWebkitVersion"> + Qt Webkit Version: 4.5.2 + </floater.string> + <floater.string name="PacketsLost"> Packets Lost: [LOST]/[IN] ([PCT]%) </floater.string> diff --git a/indra/newview/skins/default/xui/en/floater_about_land.xml b/indra/newview/skins/default/xui/en/floater_about_land.xml index cb3388ccbc..63abb63bdf 100644 --- a/indra/newview/skins/default/xui/en/floater_about_land.xml +++ b/indra/newview/skins/default/xui/en/floater_about_land.xml @@ -627,6 +627,7 @@ Go to World menu > About Land or select another parcel to show its details. length="1" enabled="false" follows="left|top|right|bottom" + handle_edit_keys_directly="true" height="115" layout="topleft" left_delta="0" @@ -1617,7 +1618,7 @@ Only large parcels can be listed in search. name="with media:" top="9" width="65"> - Media Type: + Type: </text> <combo_box height="18" @@ -1645,7 +1646,7 @@ Only large parcels can be listed in search. name="at URL:" top="29" width="65"> - Media URL: + Home URL: </text> <line_editor bottom_delta="0" @@ -1676,6 +1677,46 @@ Only large parcels can be listed in search. height="16" layout="topleft" left="10" + name="at URL:" + top="49" + width="65"> + Current URL: + </text> + <text + follows="left|top" + height="16" + layout="topleft" + left_pad="5" + name="current_url" + top_delta="0" + width="300" /> + <button + follows="left|top" + font="SansSerifSmall" + height="16" + label="Reset..." + label_selected="Reset..." + layout="topleft" + left_pad="6" + name="reset_media_url" + top_delta="0" + width="60" /> + <check_box + height="16" + label="Hide URL" + layout="topleft" + left="100" + name="hide_media_url" + tool_tip="Checking this option will hide the media url to any non-authorized viewers of this parcel information. Note this is not available for HTML types." + top="89" + width="200" /> + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" name="Description:" top="49" width="364"> @@ -1728,21 +1769,10 @@ Texture: name="replace_texture_help" top="85" width="270"> - (Objects using this texture will show the movie or -web page after you click the play arrow.) - </text> - <text - type="string" - length="1" - follows="left|top" - height="16" - layout="topleft" - left="10" - name="Options:" - top="140" - width="292"> - Media -Options: + Objects using this texture will show the movie or + web page after you click the play arrow. + + Select the thumbnail to choose a different texture. </text> <check_box height="16" @@ -1753,33 +1783,6 @@ Options: tool_tip="Checking this option will scale the content for this parcel automatically. It may be slightly slower and lower quality visually but no other texture scaling or alignment will be required." top_delta="0" width="200" /> - <check_box - height="16" - label="Loop Media" - layout="topleft" - left_delta="170" - name="media_loop" - tool_tip="Play media in a loop. When the media has finished playing, it will restart from the beginning." - top_delta="0" - width="200" /> - <check_box - height="16" - label="Hide Media URL" - layout="topleft" - left="80" - name="hide_media_url" - tool_tip="Checking this option will hide the media url to any non-authorized viewers of this parcel information. Note this is not available for HTML types." - top="160" - width="200" /> - <check_box - height="16" - label="Hide Music URL" - layout="topleft" - left_delta="170" - name="hide_music_url" - tool_tip="Checking this option will hide the music url to any non-authorized viewers of this parcel information" - top_delta="0" - width="200" /> <text type="string" length="1" @@ -1791,7 +1794,7 @@ Options: tool_tip="Size to render Web media, leave 0 for default." top="185" width="85"> - Media size: + Size: </text> <spinner decimal_digits="0" @@ -1842,6 +1845,38 @@ Options: height="16" layout="topleft" left="10" + name="Options:" + top="237" + width="292"> + Options: + </text> + <check_box + height="16" + label="Loop" + layout="topleft" + left_delta="70" + name="media_loop" + tool_tip="Play media in a loop. When the media has finished playing, it will restart from the beginning." + top_delta="0" + width="200" /> + </panel> + <panel + border="true" + follows="left|top|right|bottom" + height="363" + label="Audio" + layout="topleft" + left_delta="0" + name="land_audio_panel" + top_delta="1" + width="458"> + <text + type="string" + length="1" + follows="left|top" + height="16" + layout="topleft" + left="10" name="MusicURL:" top="225" width="364"> @@ -1993,7 +2028,7 @@ Options: layout="topleft" left_delta="0" name="limit_age_verified" - tool_tip="Ban residents who have not verified their age. See support.secondlife.com for more information." + tool_tip="Ban residents who have not verified their age. See the [SUPPORT_SITE] for more information." top_pad="4" width="278" /> <check_box diff --git a/indra/newview/skins/default/xui/en/floater_activeim.xml b/indra/newview/skins/default/xui/en/floater_activeim.xml new file mode 100644 index 0000000000..38c6d44fdf --- /dev/null +++ b/indra/newview/skins/default/xui/en/floater_activeim.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<floater + name="floater_activeim" + title="ACTIVE IM" + top="26" + left="0" + height="22" + width="350" + follows="right|bottom" + background_visible="true" + can_close="true" + can_dock="true" + can_minimize="false" + visible="true" + bg_alpha_color="0 0 0 0"> + <scroll_container + follows="top|bottom" + layout="topleft" + top="20" + left="1" + width="349" + height="2" + name="panel_list_container"> + <scrolling_panel_list + follows="left|right" + layout="topleft" + name="chiclet_row_panel_list" + width="335"/> + </scroll_container> +</floater>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/floater_buy_land.xml b/indra/newview/skins/default/xui/en/floater_buy_land.xml index 777236504d..5e0e423128 100644 --- a/indra/newview/skins/default/xui/en/floater_buy_land.xml +++ b/indra/newview/skins/default/xui/en/floater_buy_land.xml @@ -341,6 +341,7 @@ supports [AMOUNT2] objects length="1" enabled="false" follows="top|right" + handle_edit_keys_directly="false" height="237" layout="topleft" left="444" diff --git a/indra/newview/skins/default/xui/en/floater_customize.xml b/indra/newview/skins/default/xui/en/floater_customize.xml index 41bd417c12..824df082d8 100644 --- a/indra/newview/skins/default/xui/en/floater_customize.xml +++ b/indra/newview/skins/default/xui/en/floater_customize.xml @@ -3561,14 +3561,4 @@ scratch and wear it. name="Ok" right="-116" width="100" /> - <button - follows="left|bottom" - height="20" - label="Make Outfit..." - label_selected="Make Outfit..." - layout="topleft" - left_delta="-178" - name="Make Outfit" - top_delta="0" - width="100" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_env_settings.xml b/indra/newview/skins/default/xui/en/floater_env_settings.xml index 8bb67d0d4b..5aa7809208 100644 --- a/indra/newview/skins/default/xui/en/floater_env_settings.xml +++ b/indra/newview/skins/default/xui/en/floater_env_settings.xml @@ -162,13 +162,4 @@ name="EnvAdvancedWaterButton" top_delta="0" width="137" /> - <button - follows="left|top" - height="18" - label="?" - layout="topleft" - left="570" - name="EnvSettingsHelpButton" - top="22" - width="18" /> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_im_session.xml b/indra/newview/skins/default/xui/en/floater_im_session.xml index 4d7fa45a47..a102d64587 100644 --- a/indra/newview/skins/default/xui/en/floater_im_session.xml +++ b/indra/newview/skins/default/xui/en/floater_im_session.xml @@ -20,9 +20,7 @@ top="16"
left="2">
<layout_panel
- class="panel_im_control_panel"
name="panel_im_control_panel"
- filename="panel_im_control_panel.xml"
layout="topleft"
top_delta="-3"
min_width="96"
@@ -57,6 +55,7 @@ layout="topleft"
max_length="2147483647"
name="im_text"
+ track_bottom="true"
width="195"
word_wrap="true">
</text_editor>
diff --git a/indra/newview/skins/default/xui/en/floater_incoming_call.xml b/indra/newview/skins/default/xui/en/floater_incoming_call.xml index 5c4649276a..2e7f5e6c42 100644 --- a/indra/newview/skins/default/xui/en/floater_incoming_call.xml +++ b/indra/newview/skins/default/xui/en/floater_incoming_call.xml @@ -8,6 +8,10 @@ name="incoming call" title="Unknown Person is calling" width="240"> + <floater.string + name="anonymous"> + anonymous + </floater.string> <floater.string name="VoiceInviteP2P"> is calling. diff --git a/indra/newview/skins/default/xui/en/floater_inventory.xml b/indra/newview/skins/default/xui/en/floater_inventory.xml index 610c62a21a..37c6cbf391 100644 --- a/indra/newview/skins/default/xui/en/floater_inventory.xml +++ b/indra/newview/skins/default/xui/en/floater_inventory.xml @@ -207,22 +207,6 @@ parameter="category" /> </menu_item_call> <menu_item_call - label="New Outfit" - layout="topleft" - name="New Outfit"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="outfit" /> - </menu_item_call> - <menu_item_call - label="New My Outfits" - layout="topleft" - name="New My Outfits"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="my_otfts" /> - </menu_item_call> - <menu_item_call label="New Script" layout="topleft" name="New Script"> diff --git a/indra/newview/skins/default/xui/en/floater_land_holdings.xml b/indra/newview/skins/default/xui/en/floater_land_holdings.xml index 0a97a96ee3..60677ca0df 100644 --- a/indra/newview/skins/default/xui/en/floater_land_holdings.xml +++ b/indra/newview/skins/default/xui/en/floater_land_holdings.xml @@ -52,8 +52,8 @@ <button height="20" font="SansSerifSmall" - label="Show on Map" - label_selected="Show on Map" + label="Map" + label_selected="Map" layout="topleft" left_pad="4" name="Show on Map" diff --git a/indra/newview/skins/default/xui/en/floater_media_browser.xml b/indra/newview/skins/default/xui/en/floater_media_browser.xml index a277294be1..afc72a78a9 100644 --- a/indra/newview/skins/default/xui/en/floater_media_browser.xml +++ b/indra/newview/skins/default/xui/en/floater_media_browser.xml @@ -9,71 +9,55 @@ save_rect="true" title="Media Browser" width="820"> - <!--TODO: ADD CLOSE X TO WINDOW--> - <!-- <button - follows="bottom|right" - height="20" - label="Close" - layout="topleft" - left_pad="80" - name="close" - top_delta="0" - width="70"> - <button.commit_callback - function="MediaBrowser.Close" /> - </button>--> + <floater.string + name="home_page_url"> + http://www.secondlife.com + </floater.string> + <floater.string + name="support_page_url"> + http://support.secondlife.com + </floater.string> <layout_stack + bottom="440" follows="left|right|top|bottom" layout="topleft" left="10" name="stack1" top="20" - width="800" - height="400" - > + width="800"> <layout_panel auto_resize="false" + height="20" layout="topleft" + left="0" name="nav_controls" - user_resize="false"> + top="400" + user_resize="false" + width="800"> <button - left="1" - follows="left|top" - font="SansSerifSmall" - height="20" - image_disabled="PushButton_Disabled" - image_disabled_selected="PushButton_Disabled" - image_overlay="Arrow_Left_Off" - image_selected="PushButton_Selected" - image_unselected="PushButton_Off" - hover_glow_amount="0.15" - layout="topleft" - name="back" - picture_style="true" - tool_tip="Go back to previous location" - width="20"> - <button.commit_callback - function="MediaBrowser.Back" /> - </button> + follows="left|top" + height="20" + label="Back" + layout="topleft" + left="0" + name="back" + top="0" + width="55"> + <button.commit_callback + function="MediaBrowser.Back" /> + </button> <button - follows="left|top" - font="SansSerifSmall" - height="20" - image_disabled="PushButton_Disabled" - image_disabled_selected="PushButton_Disabled" - image_overlay="Arrow_Right_Off" - image_selected="PushButton_Selected" - image_unselected="PushButton_Off" - hover_glow_amount="0.15" - layout="topleft" - left_pad="2" - name="forward" - picture_style="true" - tool_tip="Go forward one location" - width="20"> - <button.commit_callback - function="MediaBrowser.Forward" /> - </button> + follows="left|top" + height="20" + label="Forward" + layout="topleft" + left_pad="3" + name="forward" + top_delta="0" + width="68"> + <button.commit_callback + function="MediaBrowser.Forward" /> + </button> <button enabled="false" follows="left|top" @@ -82,26 +66,24 @@ layout="topleft" left_pad="2" name="reload" + top_delta="0" width="70"> - <button.commit_callback - function="MediaBrowser.Refresh" /> - </button> + <button.commit_callback + function="MediaBrowser.Refresh" /> + </button> <combo_box allow_text_entry="true" + follows="left|top|right" + height="20" + layout="topleft" + left_pad="5" + max_chars="255" name="address" - follows="left|right|top" - halign="right" - height="20" - label="Location" - layout="topleft" - left_pad="5" - max_chars="254" - mouse_opaque="false" - top_delta="0" - width="340"> - <combo_box.commit_callback - function="MediaBrowser.EnterAddress" /> - </combo_box> + top_delta="0" + width="540"> + <combo_box.commit_callback + function="MediaBrowser.EnterAddress" /> + </combo_box> <button enabled="false" follows="right|top" @@ -110,31 +92,81 @@ layout="topleft" left_pad="5" name="go" - width="50"> - <button.commit_callback - function="MediaBrowser.Go" /> - </button> + top_delta="0" + width="55"> + <button.commit_callback + function="MediaBrowser.Go" /> + </button> + </layout_panel> + <layout_panel + auto_resize="false" + height="20" + layout="topleft" + left_delta="0" + name="time_controls" + top_delta="0" + user_resize="false" + width="800"> <button - follows="right|top" + follows="left|top" height="20" - left_pad="20" - label="Open in My Browser" + label="rewind" layout="topleft" - name="open_browser" - width="145"> - <button.commit_callback - function="MediaBrowser.OpenWebBrowser" /> - </button> - <check_box - control_name="UseExternalBrowser" - follows="right|top" - label="Always" + left="0" + name="rewind" + top="0" + width="55" /> + <button + follows="left|top" + height="20" + image_selected="button_anim_play_selected.tga" + image_unselected="button_anim_play.tga" layout="topleft" - left_pad="5" - width="140" - name="open_always" /> - <!-- - <button + left_delta="55" + name="play" + picture_style="true" + top_delta="0" + width="55" /> + <button + follows="left|top" + height="20" + image_selected="button_anim_pause_selected.tga" + image_unselected="button_anim_pause.tga" + layout="topleft" + left_delta="0" + name="pause" + picture_style="true" + top_delta="0" + width="55" /> + <button + follows="left|top" + height="20" + label="stop" + layout="topleft" + left_pad="10" + name="stop" + top_delta="0" + width="55" /> + <button + follows="left|top" + height="20" + label="forward" + layout="topleft" + left_pad="20" + name="seek" + top_delta="0" + width="55" /> + </layout_panel> + <layout_panel + auto_resize="false" + height="20" + layout="topleft" + left_delta="0" + name="parcel_owner_controls" + top_delta="0" + user_resize="false" + width="540"> + <button enabled="false" follows="left|top" height="20" @@ -144,22 +176,60 @@ name="assign" top="0" width="200"> - <button.commit_callback - function="MediaBrowser.Assign" /> - </button> - </layout_panel> + <button.commit_callback + function="MediaBrowser.Assign" /> + </button> + </layout_panel> <layout_panel - auto_resize="false" + height="20" layout="topleft" - name="browser" - left="0" - top="0" - user_resize="false">--> - <web_browser + left_delta="0" + name="external_controls" + top_delta="0" + user_resize="false" + width="540"> + <web_browser + bottom="-10" follows="left|right|top|bottom" layout="topleft" left="0" - name="browser" /> + name="browser" + top="0" + width="540" /> + <button + follows="bottom|left" + height="20" + label="Open in My Web Browser" + layout="topleft" + left_delta="0" + name="open_browser" + top_pad="5" + width="185"> + <button.commit_callback + function="MediaBrowser.OpenWebBrowser" /> + </button> + <check_box + control_name="UseExternalBrowser" + follows="bottom|left" + height="20" + label="Always open in my web browser" + layout="topleft" + left_pad="5" + name="open_always" + top_delta="0" + width="200" /> + <button + follows="bottom|right" + height="20" + label="Close" + layout="topleft" + left_pad="80" + name="close" + top_delta="0" + width="70"> + <button.commit_callback + function="MediaBrowser.Close" /> + </button> </layout_panel> </layout_stack> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml index 2d67e3d5a9..674bfa42da 100644 --- a/indra/newview/skins/default/xui/en/floater_nearby_chat.xml +++ b/indra/newview/skins/default/xui/en/floater_nearby_chat.xml @@ -9,6 +9,7 @@ name="nearby_chat" save_rect="true" title="Nearby Chat" + single_instance="true" width="320"> <panel top="20" width="320" height="30" background_visible="true" background_opaque="false" bg_alpha_color="0.0 0.0 0.0 1.0" name="chat_caption"> <text diff --git a/indra/newview/skins/default/xui/en/floater_postcard.xml b/indra/newview/skins/default/xui/en/floater_postcard.xml index 91c2a41891..8657663057 100644 --- a/indra/newview/skins/default/xui/en/floater_postcard.xml +++ b/indra/newview/skins/default/xui/en/floater_postcard.xml @@ -12,7 +12,7 @@ width="450"> <floater.string name="default_subject"> - Postcard from Second Life. + Postcard from [SECOND_LIFE]. </floater.string> <floater.string name="default_message"> @@ -121,6 +121,7 @@ left_delta="0" max_length="700" name="msg_form" + word_wrap="true" top_pad="10" width="420"> Type your message here. diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index d9602c75ee..bacec627e1 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -43,7 +43,7 @@ name="pref core" tab_group="1" tab_position="left" - tab_width="120" + tab_width="115" top="21" width="620"> <panel @@ -53,6 +53,7 @@ layout="topleft" name="general" /> <panel + class="panel_preference" filename="panel_preferences_graphics1.xml" label="Graphics" layout="topleft" @@ -87,8 +88,6 @@ label="Setup" layout="topleft" name="input" /> - <!-- I put back the commented out panels for now, pending - implementation of new default settings. James --> <panel class="panel_preference" filename="panel_preferences_advanced.xml" @@ -98,15 +97,9 @@ <panel class="panel_preference" filename="panel_preferences_advanced2.xml" - label="Move, not to Advanced" + label="Move or Kill" layout="topleft" name="advanced2" /> - <panel - class="panel_preference" - filename="panel_preferences_advanced4.xml" - label="Storage/Kill" - layout="topleft" - name="advanced4" /> </tab_container> - + </floater> diff --git a/indra/newview/skins/default/xui/en/floater_preview_gesture.xml b/indra/newview/skins/default/xui/en/floater_preview_gesture.xml index a463b2274d..2fd561f991 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_gesture.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_gesture.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <floater auto_tile="true" - height="400" + height="600" layout="topleft" name="gesture_preview" width="280"> @@ -48,7 +48,7 @@ name="desc_label" top="25" width="100"> - Description: + Name (not working yet): </text> <line_editor follows="left|top" @@ -66,21 +66,21 @@ height="10" layout="topleft" left="10" - name="trigger_label" - top="54" + name="desc_label" + top_pad="10" width="100"> - Trigger: + Description: </text> <line_editor follows="left|top" - height="20" + height="40" layout="topleft" left_delta="84" - max_length="31" - name="trigger_editor" + name="desc" top_delta="-4" - width="100" /> - <text + width="180" /> + <!--ACCORDION GOES HERE --> + <text type="string" length="1" follows="top|left" @@ -88,22 +88,20 @@ height="10" layout="topleft" left="10" - name="replace_text" - tool_tip="Replace the trigger word(s) with these words. For example, trigger 'hello' replace with 'howdy' will turn the chat 'I wanted to say hello' into 'I wanted to say howdy' as well as playing the gesture!" - top="79" - width="200"> - Replace with: + name="trigger_label" + top_pad="10" + width="100"> + Chat: </text> <line_editor follows="left|top" height="20" layout="topleft" - left_delta="84" + left_pad="5" max_length="31" - name="replace_editor" - tool_tip="Replace the trigger word(s) with these words. For example, trigger 'hello' replace with 'howdy' will turn the chat 'I wanted to say hello' into 'I wanted to say howdy' as well as playing the gesture" + name="trigger_editor" top_delta="-4" - width="130" /> + width="135" /> <text type="string" length="1" @@ -113,15 +111,15 @@ layout="topleft" left="10" name="key_label" - top="104" + top_pad="10" width="100"> - Shortcut Key: + Keyboard: </text> <combo_box height="20" label="None" layout="topleft" - left_delta="84" + left_pad="5" name="modifier_combo" top_delta="-4" width="50" /> @@ -129,7 +127,7 @@ height="20" label="None" layout="topleft" - left_pad="10" + left_pad="5" name="key_combo" top_delta="0" width="45" /> @@ -141,32 +139,52 @@ height="10" layout="topleft" left="10" - name="library_label" - top="135" + name="replace_text" + tool_tip="Replace the trigger word(s) with these words. For example, trigger 'hello' replace with 'howdy' will turn the chat 'I wanted to say hello' into 'I wanted to say howdy' as well as playing the gesture!" + top_pad="10" width="100"> - Library: + Replace: </text> - <text - type="string" - length="1" + <line_editor + follows="left|top" + height="20" + layout="topleft" + left_pad="5" + max_length="31" + name="replace_editor" + tool_tip="Replace the trigger word(s) with these words. For example, trigger 'hello' replace with 'howdy' will turn the chat 'I wanted to say hello' into 'I wanted to say howdy' as well as playing the gesture" + top_delta="-4" + width="135" /> + + <!--ACCORDION GOES HERE --> + <scroll_list + follows="top|left" + height="110" + layout="topleft" + left="10" + name="step_list" + top_pad="10" + width="260" /> + <button follows="top|left" + height="20" font="SansSerifSmall" - height="10" + label="Add" layout="topleft" - left_pad="75" - name="steps_label" - top_delta="0" - width="100"> - Steps: - </text> + left_pad="-100" + name="add_btn" + top_pad="5" + width="100" /> + + <scroll_list follows="top|left" height="110" layout="topleft" left="10" name="library_list" - top="150" - width="80"> + top_pad="10" + width="260"> <scroll_list.rows value="Animation" /> <scroll_list.rows @@ -176,54 +194,39 @@ <scroll_list.rows value="Wait" /> </scroll_list> + + + <button follows="top|left" height="20" - font="SansSerifSmall" - label="Add >>" - layout="topleft" - left_pad="14" - name="add_btn" - top_delta="0" - width="70" /> - <button - follows="top|left" - height="20" - font="SansSerifSmall" - label="Up" + label="Preview" layout="topleft" - left_delta="0" - name="up_btn" - top_pad="10" - width="70" /> + left="20" + name="preview_btn" + top_pad="50" + width="80" /> <button follows="top|left" height="20" - font="SansSerifSmall" - label="Down" + label="Save" layout="topleft" - left_delta="0" - name="down_btn" - top_pad="10" - width="70" /> + left_pad="5" + name="save_btn" + top_delta="0" + width="80" /> <button follows="top|left" height="20" - font="SansSerifSmall" - label="Remove" - layout="topleft" - left_delta="0" - name="delete_btn" - top_pad="10" - width="70" /> - <scroll_list - follows="top|left" - height="110" + label="Cancel (not working)" layout="topleft" - left="185" - name="step_list" - top="150" - width="85" /> + left_pad="5" + name="save_btn" + top_delta="0" + width="80" /> + + + <text type="string" length="1" @@ -233,7 +236,7 @@ layout="topleft" left="10" name="help_label" - top="270" + top_pad="10" width="200"> All steps happen simultaneously, unless you add wait steps. @@ -244,7 +247,7 @@ unless you add wait steps. layout="topleft" left="10" name="options_text" - top="300" + top_pad="10" width="205" /> <combo_box follows="top|left" @@ -252,7 +255,7 @@ unless you add wait steps. layout="topleft" left_delta="10" name="animation_list" - top="320" + top_pad="10" width="100" /> <combo_box follows="top|left" @@ -278,7 +281,7 @@ unless you add wait steps. layout="topleft" left_pad="8" name="animation_trigger_type" - top_delta="0" + top="445" width="80"> <radio_item height="16" @@ -304,7 +307,7 @@ unless you add wait steps. layout="topleft" left="16" name="wait_anim_check" - top="315" + top="430" width="100" /> <check_box follows="top|left" @@ -324,6 +327,73 @@ unless you add wait steps. name="wait_time_editor" top_delta="0" width="50" /> + + + + <!--accordion + layout="topleft" + left="2" + width="280" + top_pad="10" + height="600" + follows="all" + name="preview_gesture_shortcuts"> + <accordion_tab + min_height="515" + title="Shortcuts" + name="preview_gesture_shortcuts"> + <panel + class="preview_gesture_shortcuts" + filename="floater_preview_gesture_shortcuts" + name="preview_gesture_shortcuts" /> + </accordion_tab> + <accordion_tab + min_height="380" + title="Group Roles" + name="group_roles_tab" + can_resize="false"> + <panel + class="panel_group_roles" + filename="panel_group_roles.xml" + name="group_roles_tab_panel"/> + </accordion_tab> + <accordion_tab + min_height="530" + title="Group Notices" + name="group_notices_tab" + can_resize="false"> + <panel + class="panel_group_notices" + filename="panel_group_notices.xml" + name="group_notices_tab_panel"/> + </accordion_tab> + <accordion_tab + min_height="270" + title="Group Land Money" + name="group_land_tab" + can_resize="false"> + <panel + class="panel_group_land_money" + filename="panel_group_land_money.xml" + name="group_land_tab_panel"/> + </accordion_tab> + </accordion--> + + + + <!--text + type="string" + length="1" + follows="top|left" + font="SansSerifSmall" + height="10" + layout="topleft" + left="10" + name="library_label" + top_pad="10" + width="100"> + Library: + </text> <check_box follows="top|left" height="20" @@ -333,23 +403,5 @@ unless you add wait steps. name="active_check" tool_tip="Active gestures can be triggered by chatting their trigger phrases or pressing their hot keys. Gestures usually become inactive when there is a key binding conflict." top="365" - width="100" /> - <button - follows="top|left" - height="20" - label="Preview" - layout="topleft" - left_delta="75" - name="preview_btn" - top_delta="2" - width="80" /> - <button - follows="top|left" - height="20" - label="Save" - layout="topleft" - left_pad="10" - name="save_btn" - top_delta="0" - width="80" /> + width="100" /--> </floater>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml index f8f1abd179..4595c9eeda 100644 --- a/indra/newview/skins/default/xui/en/floater_preview_notecard.xml +++ b/indra/newview/skins/default/xui/en/floater_preview_notecard.xml @@ -74,6 +74,7 @@ left="4" max_length="65536" name="Notecard Editor" + handle_edit_keys_directly="true" tab_group="1" top="46" width="392" diff --git a/indra/newview/skins/default/xui/en/floater_stats.xml b/indra/newview/skins/default/xui/en/floater_stats.xml index 08d91843f2..4d7da97f79 100644 --- a/indra/newview/skins/default/xui/en/floater_stats.xml +++ b/indra/newview/skins/default/xui/en/floater_stats.xml @@ -25,6 +25,668 @@ left="2" name="statistics_view" top="20" - width="230" /> + width="230" > + <stat_view + name="basic" + label="Basic" + show_label="true" + setting="OpenDebugStatBasic"> + <stat_bar + name="fps" + label="FPS" + unit_label="fps" + stat="fpsstat" + bar_min="0" + bar_max="45" + tick_spacing="7.5" + label_spacing="15.0" + precision="1" + show_bar="true" + show_history="true"> + </stat_bar> + <stat_bar + name="bandwidth" + label="Bandwidth" + unit_label="kbps" + stat="kbitstat" + bar_min="0" + bar_max="900" + tick_spacing="100" + label_spacing="300" + show_bar="true" + show_history="false"> + </stat_bar> + <stat_bar + name="packet_loss" + label="Packet Loss" + unit_label=" %" + stat="packetslostpercentstat" + bar_min="0" + bar_max="5" + tick_spacing="1" + label_spacing="1" + precision="1" + show_per_sec="false" + show_bar="false" + show_mean="true"> + </stat_bar> + <stat_bar + name="ping" + label="Ping Sim" + unit_label="msec" + stat="simpingstat" + bar_min="0" + bar_max="1000" + tick_spacing="100" + label_spacing="200" + show_bar="false" + show_per_sec="false" + show_mean="false"> + </stat_bar> + </stat_view> + <stat_view + name="advanced" + label="Advanced" + show_label="true" + setting="OpenDebugStatAdvanced"> + <stat_view + name="render" + label="Render" + show_label="true" + setting="OpenDebugStatRender"> + <stat_bar + name="ktrisframe" + label="KTris Drawn" + unit_label="/fr" + stat="trianglesdrawnstat" + bar_min="0" + bar_max="500" + tick_spacing="100" + label_spacing="500" + precision="1" + show_per_sec="false"> + </stat_bar> + <stat_bar + name="ktrissec" + label="KTris Drawn" + unit_label="/sec" + stat="trianglesdrawnstat" + bar_min="0" + bar_max="3000" + tick_spacing="250" + label_spacing="1000" + precision="1"> + </stat_bar> + <stat_bar + name="objs" + label="Total Objects" + unit_label="" + stat="numobjectsstat" + bar_min="0" + bar_max="3000" + tick_spacing="250" + label_spacing="1000" + precision="1" + show_per_sec="false" + show_bar="false"> + </stat_bar> + <stat_bar + name="newobjs" + label="New Objects" + unit_label="/sec" + stat="numnewobjectsstat" + bar_min="0" + bar_max="1000" + tick_spacing="100" + label_spacing="500" + show_per_sec="true" + show_bar="false"> + </stat_bar> + </stat_view> + <stat_view + name="texture" + label="Texture" + show_label="true"> + <stat_bar + name="numimagesstat" + label="Count" + stat="numimagesstat" + bar_min="0.f" + bar_max="8000.f" + tick_spacing="2000.f" + label_spacing="4000.f" + show_per_sec="false" + show_bar="false"> + </stat_bar> + + <stat_bar + name="numrawimagesstat" + label="Raw Count" + stat="numrawimagesstat" + bar_min="0.f" + bar_max="8000.f" + tick_spacing="2000.f" + label_spacing="4000.f" + show_per_sec="false" + show_bar="false"> + </stat_bar> + + <stat_bar + name="gltexmemstat" + label="GL Mem" + stat="gltexmemstat" + bar_min="0.f" + bar_max="400.f" + tick_spacing="100.f" + label_spacing="200.f" + precision="1" + show_per_sec="false" > + </stat_bar> + + <stat_bar + name="formattedmemstat" + label="Formatted Mem" + stat="formattedmemstat" + bar_min="0.f" + bar_max="400.f" + tick_spacing="100.f" + label_spacing="200.f" + precision="1" + show_per_sec="false" > + </stat_bar> + + <stat_bar + name="rawmemstat" + label="Raw Mem" + stat="rawmemstat" + bar_min="0.f" + bar_max="400.f" + tick_spacing="100.f" + label_spacing="200.f" + precision="1" + show_per_sec="false" > + </stat_bar> + + <stat_bar + name="glboundmemstat" + label="Bound Mem" + stat="glboundmemstat" + bar_min="0.f" + bar_max="400.f" + tick_spacing="100.f" + label_spacing="200.f" + precision="1" + show_per_sec="false" > + </stat_bar> + </stat_view> + + <stat_view + name="network" + label="Network" + show_label="true" + setting="OpenDebugStatNet"> + <stat_bar + name="packetsinstat" + label="Packets In" + stat="packetsinstat" + unit_label="/sec" + show_bar="false"> + </stat_bar> + + <stat_bar + name="packetsoutstat" + label="Packets Out" + stat="packetsoutstat" + unit_label="/sec" + show_bar="false" > + </stat_bar> + + <stat_bar + name="objectkbitstat" + label="Objects" + stat="objectkbitstat" + unit_label="kbps" + show_bar="false" > + </stat_bar> + + <stat_bar + name="texturekbitstat" + label="Texture" + stat="texturekbitstat" + unit_label="kbps" + show_bar="false" > + </stat_bar> + + <stat_bar + name="assetkbitstat" + label="Asset" + stat="assetkbitstat" + unit_label="kbps" + show_bar="false" > + </stat_bar> + + <stat_bar + name="layerskbitstat" + label="Layers" + stat="layerskbitstat" + unit_label="kbps" + show_bar="false" > + </stat_bar> + + <stat_bar + name="actualinkbitstat" + label="Actual In" + stat="actualinkbitstat" + unit_label="kbps" + bar_min="0.f" + bar_max="1024.f" + tick_spacing="128.f" + label_spacing="256.f" + show_bar="true" + show_history="false" > + </stat_bar> + + <stat_bar + name="actualoutkbitstat" + label="Actual Out" + stat="actualoutkbitstat" + unit_label="kbps" + bar_min="0.f" + bar_max="512.f" + tick_spacing="128.f" + label_spacing="256.f" + show_bar="true" + show_history="false"> + </stat_bar> + + <stat_bar + name="vfspendingoperations" + label="VFS Pending Ops" + stat="vfspendingoperations" + unit_label=" " + show_per_sec="false" + show_bar="false" > + </stat_bar> + </stat_view> + </stat_view> + + <stat_view + name="sim" + label="Simulator" + show_label="true" + setting="OpenDebugStatSim"> + <stat_bar + name="simtimedilation" + label="Time Dilation" + stat="simtimedilation" + precision="2" + bar_min="0.f" + bar_max="1.f" + tick_spacing="0.25f" + label_spacing="0.5f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simfps" + label="Sim FPS" + stat="simfps" + bar_min="0.f" + bar_max="200.f" + tick_spacing="20.f" + label_spacing="100.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simphysicsfps" + label="Physics FPS" + stat="simphysicsfps" + precision="1" + bar_min="0.f" + bar_max="66.f" + tick_spacing="33.f" + label_spacing="33.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_view + name="physicsdetail" + label="Physics Details" + show_label="true" + display_children="false"> + <stat_bar + name="physicspinnedtasks" + label="Pinned Objects" + stat="physicspinnedtasks" + precision="0" + bar_min="0.f" + bar_max="500.f" + tick_spacing="10.f" + label_spacing="40.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="physicslodtasks" + label="Low LOD Objects" + stat="physicslodtasks" + precision="0" + bar_min="0.f" + bar_max="500.f" + tick_spacing="10.f" + label_spacing="40.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="physicsmemoryallocated" + label="Memory Allocated" + stat="physicsmemoryallocated" + unit_label="MB" + precision="0" + bar_min="0.f" + bar_max="1024.f" + tick_spacing="128.f" + label_spacing="256.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simagentups" + label="Agent Updates/Sec" + stat="simagentups" + precision="1" + bar_min="0.f" + bar_max="100.f" + tick_spacing="25.f" + label_spacing="50.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simmainagents" + label="Main Agents" + stat="simmainagents" + precision="0" + bar_min="0.f" + bar_max="80.f" + tick_spacing="10.f" + label_spacing="40.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simchildagents" + label="Child Agents" + stat="simchildagents" + precision="0" + bar_min="0.f" + bar_max="40.f" + tick_spacing="5.f" + label_spacing="10.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simobjects" + label="Objects" + stat="simobjects" + precision="0" + bar_min="0.f" + bar_max="30000.f" + tick_spacing="5000.f" + label_spacing="10000.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simactiveobjects" + label="Active Objects" + stat="simactiveobjects" + precision="0" + bar_min="0.f" + bar_max="800.f" + tick_spacing="100.f" + label_spacing="200.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simactivescripts" + label="Active Scripts" + stat="simactivescripts" + precision="0" + bar_min="0.f" + bar_max="800.f" + tick_spacing="100.f" + label_spacing="200.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simscripteps" + label="Script Events" + stat="simscripteps" + unit_label="eps" + precision="0" + bar_min="0.f" + bar_max="20000.f" + tick_spacing="2500.f" + label_spacing="5000.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="siminpps" + label="Packets In" + stat="siminpps" + unit_label="pps" + precision="0" + bar_min="0.f" + bar_max="2000.f" + tick_spacing="250.f" + label_spacing="1000.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simoutpps" + label="Packets Out" + stat="simoutpps" + unit_label="pps" + precision="0" + bar_min="0.f" + bar_max="2000.f" + tick_spacing="250.f" + label_spacing="1000.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simpendingdownloads" + label="Pending Downloads" + stat="simpendingdownloads" + precision="0" + bar_min="0.f" + bar_max="800.f" + tick_spacing="100.f" + label_spacing="200.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simpendinguploads" + label="Pending Uploads" + stat="simpendinguploads" + precision="0" + bar_min="0.f" + bar_max="100.f" + tick_spacing="25.f" + label_spacing="50.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simtotalunackedbytes" + label="Total Unacked Bytes" + stat="simtotalunackedbytes" + unit_label="kb" + precision="0" + bar_min="0.f" + bar_max="100000.f" + tick_spacing="25000.f" + label_spacing="50000.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + </stat_view> + + <stat_view + name="simperf" + label="Time (ms)" + show_label="true"> + <stat_bar + name="simframemsec" + label="Total Frame Time" + stat="simframemsec" + unit_label="ms" + precision="1" + bar_min="0.f" + bar_max="40.f" + tick_spacing="10.f" + label_spacing="20.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simnetmsec" + label="Net Time" + stat="simnetmsec" + unit_label="ms" + precision="1" + bar_min="0.f" + bar_max="40.f" + tick_spacing="10.f" + label_spacing="20.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simsimphysicsmsec" + label="Physics Time" + stat="simsimphysicsmsec" + unit_label="ms" + precision="1" + bar_min="0.f" + bar_max="40.f" + tick_spacing="10.f" + label_spacing="20.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simsimothermsec" + label="Simulation Time" + stat="simsimothermsec" + unit_label="ms" + precision="1" + bar_min="0.f" + bar_max="40.f" + tick_spacing="10.f" + label_spacing="20.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simagentmsec" + label="Agent Time" + stat="simagentmsec" + unit_label="ms" + precision="1" + bar_min="0.f" + bar_max="40.f" + tick_spacing="10.f" + label_spacing="20.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simimagesmsec" + label="Images Time" + stat="simimagesmsec" + unit_label="ms" + precision="1" + bar_min="0.f" + bar_max="40.f" + tick_spacing="10.f" + label_spacing="20.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + + <stat_bar + name="simscriptmsec" + label="Script Time" + stat="simscriptmsec" + unit_label="ms" + precision="1" + bar_min="0.f" + bar_max="40.f" + tick_spacing="10.f" + label_spacing="20.f" + show_per_sec="false" + show_bar="false" + show_mean="false" > + </stat_bar> + </stat_view> + </stat_view> + </container_view> </scroll_container> </floater> diff --git a/indra/newview/skins/default/xui/en/floater_sys_well.xml b/indra/newview/skins/default/xui/en/floater_sys_well.xml index 468d41e2f0..46c8960fbd 100644 --- a/indra/newview/skins/default/xui/en/floater_sys_well.xml +++ b/indra/newview/skins/default/xui/en/floater_sys_well.xml @@ -4,96 +4,37 @@ background_visible="true" bevel_style="in" bg_alpha_color="0.0 0.0 0.0 0.0" - height="60" + height="30" left="0" top="0" follows="right|bottom" layout="topleft" name="notification_chiclet" save_rect="true" - title="" + title="NOTIFICATIONS" width="320" + min_width="320" can_minimize="false" can_tear_off="false" can_resize="false" can_drag_on_left="false" can_close="false" - can_dock="false" + can_dock="true" > <scroll_container follows="top|bottom" layout="topleft" name="notification_list_container" - left="1" - top="30" - width="336" - height="30"> + left="0" + top="18" + width="320" + height="12"> <scrolling_panel_list - follows="left|right" layout="topleft" name="notification_list" - left="0" + left="1" top="0" - height="20" - width="320" /> + height="10" + width="318" /> </scroll_container> - - <panel - top="0" - width="320" - height="30" - layout="topleft" - follows="top|left|right" - background_visible="true" - background_opaque="false" - bg_alpha_color="0.0 0.0 0.0 1.0" - name="notification_caption" - > - <text - width="255" - left="25" - height="20" - layout="topleft" - follows="left|right|top" - font="SansSerifBoldBig" - text_color="white" - word_wrap="true" - mouse_opaque="true" - name="sender_name" - > - NOTIFICATIONS - </text> - <button - top="5" - left="270" - width="15" - height="15" - layout="topleft" - follows="right" - label="" - toggle="true" - image_unselected="arrow_up.tga" - image_disabled="arrow_up.tga" - image_selected="arrow_down.tga" - image_hover_selected="arrow_down.tga" - image_disabled_selected="arrow_down.tga" - name="tear_btn" - /> - <button - top="5" - left="300" - width="15" - height="15" - layout="topleft" - follows="right" - label="" - image_unselected="closebox.tga" - image_disabled="closebox.tga" - image_selected="closebox.tga" - image_hover_selected="closebox.tga" - image_disabled_selected="closebox.tga" - name="close_btn" - /> - </panel> - </floater> diff --git a/indra/newview/skins/default/xui/en/floater_tos.xml b/indra/newview/skins/default/xui/en/floater_tos.xml index 6585667c8b..54facbb659 100644 --- a/indra/newview/skins/default/xui/en/floater_tos.xml +++ b/indra/newview/skins/default/xui/en/floater_tos.xml @@ -49,7 +49,7 @@ name="tos_heading" top_delta="-399" width="552"> - Please read the following Terms of Service carefully. To continue logging in to Second Life, + Please read the following Terms of Service carefully. To continue logging in to [SECOND_LIFE], you must accept the agreement. </text> <text_editor @@ -64,6 +64,7 @@ you must accept the agreement. name="tos_text" top_pad="43" width="568" + handle_edit_keys_directly="true" word_wrap="true"> TOS_TEXT </text_editor> diff --git a/indra/newview/skins/default/xui/en/floater_world_map.xml b/indra/newview/skins/default/xui/en/floater_world_map.xml index 6a022e8966..e46588dd71 100644 --- a/indra/newview/skins/default/xui/en/floater_world_map.xml +++ b/indra/newview/skins/default/xui/en/floater_world_map.xml @@ -359,8 +359,6 @@ label="Landmarks" name="item1" value="None" /> - <combo_box.commit_callback - function="WMap.Landmark" /> </combo_box> <icon color="0.5 0 0 1" diff --git a/indra/newview/skins/default/xui/en/menu_favorites.xml b/indra/newview/skins/default/xui/en/menu_favorites.xml index 7e4bbe3c9d..76c132aeb7 100644 --- a/indra/newview/skins/default/xui/en/menu_favorites.xml +++ b/indra/newview/skins/default/xui/en/menu_favorites.xml @@ -7,27 +7,59 @@ <menu_item_call label="Teleport" layout="topleft" - name="Landmark Open"> + name="Teleport To Landmark"> <menu_item_call.on_click function="Favorites.DoToSelected" parameter="open" /> </menu_item_call> <menu_item_call - label="About Landmark" + label="View/Edit Landmark" layout="topleft" - name="Teleport To Landmark"> + name="Landmark Open"> <menu_item_call.on_click function="Favorites.DoToSelected" parameter="about" /> </menu_item_call> -<!-- <menu_item_call --> -<!-- label="Rename" --> -<!-- layout="topleft" --> -<!-- name="Rename"> --> -<!-- <menu_item_call.on_click --> -<!-- function="Favorites.DoToSelected" --> -<!-- parameter="rename" /> --> -<!-- </menu_item_call> --> + <menu_item_call + label="Copy SLURL" + layout="topleft" + name="Copy slurl"> + <menu_item_call.on_click + function="Favorites.DoToSelected" + parameter="copy_slurl" /> + </menu_item_call> + <menu_item_call + label="Show On Map" + layout="topleft" + name="Show On Map"> + <menu_item_call.on_click + function="Favorites.DoToSelected" + parameter="show_on_map" /> + </menu_item_call> + + <menu_item_separator + layout="topleft" /> + + <menu_item_call + label="Copy" + layout="topleft" + name="Landmark Copy"> + <menu_item_call.on_click + function="Favorites.DoToSelected" + parameter="copy" /> + </menu_item_call> + <menu_item_call + label="Paste" + layout="topleft" + name="Landmark Paste"> + <menu_item_call.on_click + function="Favorites.DoToSelected" + parameter="paste" /> + </menu_item_call> + + <menu_item_separator + layout="topleft" /> + <menu_item_call label="Delete" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/menu_hide_navbar.xml b/indra/newview/skins/default/xui/en/menu_hide_navbar.xml new file mode 100644 index 0000000000..1ad10abbeb --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_hide_navbar.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu + height="201" + layout="topleft" + left="100" + mouse_opaque="false" + name="hide_navbar_menu" + top="624" + visible="false" + width="128"> + <menu_item_check + label="Show Navigation Bar" + layout="topleft" + name="Show Navigation Bar"> + <menu_item_check.on_click + function="HideNavbarMenu.Action" + parameter="show_navbar_navigation_panel" /> + <menu_item_check.on_check + function="HideNavbarMenu.EnableMenuItem" + parameter="show_navbar_navigation_panel" /> + </menu_item_check> + <menu_item_check + label="Show Favorites Bar" + layout="topleft" + name="Show Favorites Bar"> + <menu_item_check.on_click + function="HideNavbarMenu.Action" + parameter="show_navbar_favorites_panel" /> + <menu_item_check.on_check + function="HideNavbarMenu.EnableMenuItem" + parameter="show_navbar_favorites_panel" /> + </menu_item_check> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_imchiclet_p2p.xml b/indra/newview/skins/default/xui/en/menu_imchiclet_p2p.xml index bad6e1e212..c205868429 100644 --- a/indra/newview/skins/default/xui/en/menu_imchiclet_p2p.xml +++ b/indra/newview/skins/default/xui/en/menu_imchiclet_p2p.xml @@ -32,12 +32,4 @@ function="IMChicletMenu.Action" parameter="add" /> </menu_item_call> - <menu_item_call - label="Remove Friend..." - layout="topleft" - name="Remove Friend"> - <menu_item_call.on_click - function="IMChicletMenu.Action" - parameter="remove" /> - </menu_item_call> </menu> diff --git a/indra/newview/skins/default/xui/en/menu_inventory.xml b/indra/newview/skins/default/xui/en/menu_inventory.xml index dd8acea4ed..7762a7f667 100644 --- a/indra/newview/skins/default/xui/en/menu_inventory.xml +++ b/indra/newview/skins/default/xui/en/menu_inventory.xml @@ -77,30 +77,6 @@ parameter="category" /> </menu_item_call> <menu_item_call - label="New Current" - layout="topleft" - name="New Current"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="current" /> - </menu_item_call> - <menu_item_call - label="New Outfit" - layout="topleft" - name="New Outfit"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="outfit" /> - </menu_item_call> - <menu_item_call - label="New My Outfits" - layout="topleft" - name="New My Outfits"> - <menu_item_call.on_click - function="Inventory.DoCreate" - parameter="my_otfts" /> - </menu_item_call> - <menu_item_call label="New Script" layout="topleft" name="New Script"> @@ -338,14 +314,6 @@ function="Inventory.DoToSelected" parameter="change_folder_type_undershirt" /> </menu_item_call> - <menu_item_call - label="Outfit" - layout="topleft" - name="Outfit"> - <menu_item_call.on_click - function="Inventory.DoToSelected" - parameter="change_folder_type_outfit" /> - </menu_item_call> </menu> <menu_item_call label="Teleport" diff --git a/indra/newview/skins/default/xui/en/menu_landmark.xml b/indra/newview/skins/default/xui/en/menu_landmark.xml new file mode 100644 index 0000000000..9263f75d24 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_landmark.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + height="201" + layout="topleft" + mouse_opaque="false" + name="landmark_overflow_menu" + width="128"> + <menu_item_call + label="Copy SLURL" + layout="topleft" + name="copy"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="copy" /> + </menu_item_call> + <menu_item_call + label="Delete" + layout="topleft" + name="delete"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="delete" /> + </menu_item_call> + <menu_item_call + label="Create Pick" + layout="topleft" + name="pick"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="pick" /> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_login.xml b/indra/newview/skins/default/xui/en/menu_login.xml index 4e3c0d0c40..642087baff 100644 --- a/indra/newview/skins/default/xui/en/menu_login.xml +++ b/indra/newview/skins/default/xui/en/menu_login.xml @@ -43,7 +43,7 @@ layout="topleft" name="Help"> <menu_item_call - label="Second Life Help" + label="[SECOND_LIFE] Help" layout="topleft" name="Second Life Help" shortcut="F1"> @@ -52,7 +52,7 @@ parameter="help f1" /> </menu_item_call> <menu_item_call - label="About Second Life" + label="About [APP_NAME]" layout="topleft" name="About Second Life"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/menu_navbar.xml b/indra/newview/skins/default/xui/en/menu_navbar.xml index 435d928f00..89469fb013 100644 --- a/indra/newview/skins/default/xui/en/menu_navbar.xml +++ b/indra/newview/skins/default/xui/en/menu_navbar.xml @@ -19,16 +19,15 @@ function="Navbar.EnableMenuItem" parameter="show_coordinates" /> </menu_item_check> + <!-- Label of 'Landmark' item is changing in runtime, + see AddLandmarkNavBarMenu/EditLandmarkNavBarMenu in strings.xml --> <menu_item_call - label="Add Landmark..." + label="Landmark" layout="topleft" - name="Add Landmark"> + name="Landmark"> <menu_item_call.on_click function="Navbar.Action" parameter="landmark" /> - <menu_item_call.on_enable - function="Navbar.EnableMenuItem" - parameter="can_landmark" /> </menu_item_call> <menu_item_separator layout="topleft" diff --git a/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml new file mode 100644 index 0000000000..bb5a4e51f7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_friends_view_sort.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" color="MenuDefaultBgColor" drop_shadow="true"> + <menu_item_call name="sort_name" label="Sort by Name"> + <menu_item_call.on_click function="People.Friends.ViewSort.Action" userdata="sort_name" /> + </menu_item_call> + <menu_item_call name="sort_status" label="Sort by Status"> + <menu_item_call.on_click function="People.Friends.ViewSort.Action" userdata="sort_status" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="view_icons" label="View People Icons"> + <menu_item_call.on_click function="People.Friends.ViewSort.Action" userdata="view_icons" /> + </menu_item_call> + <menu_item_call name="organize_offline" label="Organize Offline Friends"> + <menu_item_call.on_click function="People.Friends.ViewSort.Action" userdata="organize_offline" /> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml new file mode 100644 index 0000000000..8c2c5e8c9e --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_nearby_view_sort.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" color="MenuDefaultBgColor" drop_shadow="true"> + <menu_item_call name="sort_recent" label="Sort by Recent Speakers"> + <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="sort_recent" /> + </menu_item_call> + <menu_item_call name="sort_name" label="Sort by Name"> + <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="sort_name" /> + </menu_item_call> + <menu_item_call name="sort_distance" label="Sort by Distance"> + <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="sort_distance" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="view_icons" label="View People Icons"> + <menu_item_call.on_click function="People.Nearby.ViewSort.Action" userdata="view_icons" /> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml new file mode 100644 index 0000000000..00cf443cc6 --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_people_recent_view_sort.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<menu name="menu_group_plus" + left="0" bottom="0" visible="false" + mouse_opaque="false" opaque="true" color="MenuDefaultBgColor" drop_shadow="true"> + <menu_item_call name="sort_most" label="Sort by Most Speakers"> + <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="sort_most" /> + </menu_item_call> + <menu_item_call name="sort_name" label="Sort by Name"> + <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="sort_name" /> + </menu_item_call> + <menu_item_separator layout="topleft" /> + <menu_item_call name="view_icons" label="View People Icons"> + <menu_item_call.on_click function="People.Recent.ViewSort.Action" userdata="view_icons" /> + </menu_item_call> +</menu> diff --git a/indra/newview/skins/default/xui/en/menu_picks.xml b/indra/newview/skins/default/xui/en/menu_picks.xml index 146d7d06fe..7e07a97016 100644 --- a/indra/newview/skins/default/xui/en/menu_picks.xml +++ b/indra/newview/skins/default/xui/en/menu_picks.xml @@ -25,7 +25,7 @@ function="Pick.Teleport" /> </menu_item_call> <menu_item_call - label="Show on Map" + label="Map" layout="topleft" name="pick_map"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/menu_place.xml b/indra/newview/skins/default/xui/en/menu_place.xml new file mode 100644 index 0000000000..01f62985ca --- /dev/null +++ b/indra/newview/skins/default/xui/en/menu_place.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<toggleable_menu + height="201" + layout="topleft" + mouse_opaque="false" + name="place_overflow_menu" + width="128"> + <menu_item_call + label="Make a Landmark" + layout="topleft" + name="landmark"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="landmark" /> + </menu_item_call> + <menu_item_call + label="Create Pick" + layout="topleft" + name="pick"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="pick" /> + </menu_item_call> + <menu_item_separator + layout="topleft"/> + <menu_item_call + enabled="false" + label="Buy Pass" + layout="topleft" + name="pass"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="pass" /> + </menu_item_call> + <menu_item_separator + layout="topleft"/> + <menu_item_call + enabled="false" + label="Edit" + layout="topleft" + name="edit"> + <menu_item_call.on_click + function="Places.OverflowMenu.Action" + parameter="edit" /> + </menu_item_call> +</toggleable_menu> diff --git a/indra/newview/skins/default/xui/en/menu_slurl.xml b/indra/newview/skins/default/xui/en/menu_slurl.xml index d5d9d3c43d..ee37d49946 100644 --- a/indra/newview/skins/default/xui/en/menu_slurl.xml +++ b/indra/newview/skins/default/xui/en/menu_slurl.xml @@ -27,7 +27,7 @@ parameter="about" /> </menu_item_call> <menu_item_call - label="Show on Map" + label="Map" layout="topleft" name="show_on_map"> <menu_item_call.on_click diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml index 40847b28fe..5e894e1773 100644 --- a/indra/newview/skins/default/xui/en/menu_viewer.xml +++ b/indra/newview/skins/default/xui/en/menu_viewer.xml @@ -93,7 +93,7 @@ function="Edit.EnableCustomizeAvatar" /> </menu_item_call> <menu_item_check - label="My Inventory" + label="My Things" layout="topleft" name="Inventory" shortcut="control|I"> @@ -138,7 +138,7 @@ <menu_item_separator layout="topleft" /> <menu_item_call - label="Exit Second Life" + label="Quit [APP_NAME]" layout="topleft" name="Quit" shortcut="control|Q"> @@ -173,15 +173,15 @@ </menu_item_call> <menu_item_separator layout="topleft" /> - <menu_item_call + <!--menu_item_call label="Chat" layout="topleft" name="Chat"> <menu_item_call.on_click function="World.Chat" /> - </menu_item_call> + </menu_item_call--> <menu_item_check - label="Nearby Chat" + label="Local Chat" layout="topleft" name="Nearby Chat" shortcut="control|H"> @@ -192,12 +192,11 @@ function="Floater.Toggle" parameter="nearby_chat" /> </menu_item_check> - <menu_item_separator - layout="topleft" /> <menu_item_check - label="Active Speakers" + label="Nearby Speakers" layout="topleft" - name="Active Speakers"> + name="Active Speakers" + shortcut="control|shift|A"> <menu_item_check.on_check function="Floater.Visible" parameter="active_speakers" /> @@ -270,7 +269,7 @@ <menu_item_separator layout="topleft" /> <menu_item_call - label="About Land" + label="Place Information" layout="topleft" name="About Land"> <menu_item_call.on_click @@ -370,7 +369,68 @@ </menu_item_call> <menu_item_separator layout="topleft" /> - <menu_item_check + <menu + create_jump_keys="true" + label="Sun Settings" + layout="topleft" + name="Environment Settings" + tear_off="true"> + <menu_item_call + label="Sunrise" + layout="topleft" + name="Sunrise"> + <menu_item_call.on_click + function="World.EnvSettings" + parameter="sunrise" /> + </menu_item_call> + <menu_item_call + label="Midday" + layout="topleft" + name="Noon" + shortcut="control|shift|Y"> + <menu_item_call.on_click + function="World.EnvSettings" + parameter="noon" /> + </menu_item_call> + <menu_item_call + label="Sunset" + layout="topleft" + name="Sunset" + shortcut="control|shift|N"> + <menu_item_call.on_click + function="World.EnvSettings" + parameter="sunset" /> + </menu_item_call> + <menu_item_call + label="Midnight" + layout="topleft" + name="Midnight"> + <menu_item_call.on_click + function="World.EnvSettings" + parameter="midnight" /> + </menu_item_call> + <menu_item_call + label="Use region settings" + layout="topleft" + name="Revert to Region Default"> + <menu_item_call.on_click + function="World.EnvSettings" + parameter="default" /> + </menu_item_call> + <menu_item_separator + layout="topleft" /> + <menu_item_call + label="Environment Editor" + layout="topleft" + name="Environment Editor"> + <menu_item_call.on_click + function="World.EnvSettings" + parameter="editor" /> + </menu_item_call> + </menu> + <menu_item_separator + layout="topleft" /> + <menu_item_check label="Build" layout="topleft" name="Build" @@ -389,7 +449,7 @@ name="Help" tear_off="true"> <menu_item_call - label="Second Life Help" + label="[SECOND_LIFE] Help" layout="topleft" name="Second Life Help" shortcut="F1"> @@ -433,8 +493,10 @@ function="ShowFloater" parameter="complaint reporter" /> </menu_item_call> + <menu_item_separator + layout="topleft" /> <menu_item_call - label="About Second Life" + label="About [APP_NAME]" layout="topleft" name="About Second Life"> <menu_item_call.on_click @@ -1245,65 +1307,6 @@ layout="topleft" /> <menu create_jump_keys="true" - label="Environment Settings" - layout="topleft" - name="Environment Settings" - tear_off="true"> - <menu_item_call - label="Sunrise" - layout="topleft" - name="Sunrise"> - <menu_item_call.on_click - function="World.EnvSettings" - parameter="sunrise" /> - </menu_item_call> - <menu_item_call - label="Midday" - layout="topleft" - name="Noon" - shortcut="control|shift|Y"> - <menu_item_call.on_click - function="World.EnvSettings" - parameter="noon" /> - </menu_item_call> - <menu_item_call - label="Sunset" - layout="topleft" - name="Sunset" - shortcut="control|shift|N"> - <menu_item_call.on_click - function="World.EnvSettings" - parameter="sunset" /> - </menu_item_call> - <menu_item_call - label="Midnight" - layout="topleft" - name="Midnight"> - <menu_item_call.on_click - function="World.EnvSettings" - parameter="midnight" /> - </menu_item_call> - <menu_item_call - label="Revert to Region Default" - layout="topleft" - name="Revert to Region Default"> - <menu_item_call.on_click - function="World.EnvSettings" - parameter="default" /> - </menu_item_call> - <menu_item_separator - layout="topleft" /> - <menu_item_call - label="Environment Editor" - layout="topleft" - name="Environment Editor"> - <menu_item_call.on_click - function="World.EnvSettings" - parameter="editor" /> - </menu_item_call> - </menu> - <menu - create_jump_keys="true" label="Performance Tools" layout="topleft" name="Performance Tools" @@ -1824,7 +1827,7 @@ function="Advanced.ShowDebugSettings" parameter="all" /> </menu_item_call> - <menu_item_check + <menu_item_check label="Debug (QA) Mode" layout="topleft" name="Debug Mode" diff --git a/indra/newview/skins/default/xui/en/mime_types.xml b/indra/newview/skins/default/xui/en/mime_types.xml index e3d102148a..804bee6cd1 100644 --- a/indra/newview/skins/default/xui/en/mime_types.xml +++ b/indra/newview/skins/default/xui/en/mime_types.xml @@ -7,7 +7,7 @@ none </defaultwidget> <defaultimpl> - LLMediaImplLLMozLib + media_plugin_webkit </defaultimpl> <widgetset name="web"> <label name="web_label"> @@ -55,27 +55,6 @@ true </allow_looping> </widgetset> - <widgetset name="none"> - <label name="none_label"> - No Content - </label> - <default_type> - none/none - </default_type> - <icon> - icn_media_web.tga - </icon> - <tooltip name="none_tooltip"> - No media here - </tooltip> - <playtip name="none_playtip" /> - <allow_resize> - false - </allow_resize> - <allow_looping> - false - </allow_looping> - </widgetset> <widgetset name="image"> <label name="image_label"> Image @@ -129,6 +108,9 @@ <widgettype> movie </widgettype> + <impl> + media_plugin_quicktime + </impl> </scheme> <mimetype name="blank"> <label name="blank_label"> @@ -138,7 +120,7 @@ none </widgettype> <impl> - LLMediaImplQuickTime + media_plugin_quicktime </impl> </mimetype> <mimetype name="none/none"> @@ -181,8 +163,8 @@ movie </widgettype> <impl> - LLMediaImplQuickTime - </impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype name="application/javascript"> <label name="application/javascript_label"> @@ -255,6 +237,9 @@ <widgettype> image </widgettype> + <impl> + media_plugin_flash_activex + </impl> </mimetype> <mimetype name="audio/mid"> <label name="audio/mid_label"> @@ -263,6 +248,9 @@ <widgettype> audio </widgettype> + <impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype name="audio/mpeg"> <label name="audio/mpeg_label"> @@ -271,6 +259,9 @@ <widgettype> audio </widgettype> + <impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype name="audio/x-aiff"> <label name="audio/x-aiff_label"> @@ -279,6 +270,9 @@ <widgettype> audio </widgettype> + <impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype name="audio/x-wav"> <label name="audio/x-wav_label"> @@ -287,6 +281,9 @@ <widgettype> audio </widgettype> + <impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype menu="1" name="image/bmp"> <label name="image/bmp_label"> @@ -296,7 +293,7 @@ image </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype menu="1" name="image/gif"> @@ -307,7 +304,7 @@ image </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype menu="1" name="image/jpeg"> @@ -318,7 +315,7 @@ image </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype menu="1" name="image/png"> @@ -329,7 +326,7 @@ image </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype name="image/svg+xml"> @@ -340,7 +337,7 @@ image </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype menu="1" name="image/tiff"> @@ -351,7 +348,7 @@ image </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype menu="1" name="text/html"> @@ -362,8 +359,8 @@ web </widgettype> <impl> - LLMediaImplLLMozLib - </impl> + media_plugin_webkit + </impl> </mimetype> <mimetype menu="1" name="text/plain"> <label name="text/plain_label"> @@ -373,7 +370,7 @@ text </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype name="text/xml"> @@ -384,7 +381,7 @@ text </widgettype> <impl> - LLMediaImplLLMozLib + media_plugin_webkit </impl> </mimetype> <mimetype menu="1" name="video/mpeg"> @@ -395,8 +392,8 @@ movie </widgettype> <impl> - LLMediaImplQuickTime - </impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype name="video/mp4"> <label name="video/mp4_label"> @@ -406,8 +403,8 @@ movie </widgettype> <impl> - LLMediaImplQuickTime - </impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype menu="1" name="video/quicktime"> <label name="video/quicktime_label"> @@ -417,8 +414,8 @@ movie </widgettype> <impl> - LLMediaImplQuickTime - </impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype name="video/x-ms-asf"> <label name="video/x-ms-asf_label"> @@ -428,8 +425,8 @@ movie </widgettype> <impl> - LLMediaImplQuickTime - </impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype name="video/x-ms-wmv"> <label name="video/x-ms-wmv_label"> @@ -439,8 +436,8 @@ movie </widgettype> <impl> - LLMediaImplQuickTime - </impl> + media_plugin_quicktime + </impl> </mimetype> <mimetype menu="1" name="video/x-msvideo"> <label name="video/x-msvideo_label"> @@ -450,7 +447,7 @@ movie </widgettype> <impl> - LLMediaImplQuickTime - </impl> + media_plugin_quicktime + </impl> </mimetype> </mimetypes> diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml index 17bb961308..216627879e 100644 --- a/indra/newview/skins/default/xui/en/notifications.xml +++ b/indra/newview/skins/default/xui/en/notifications.xml @@ -102,7 +102,7 @@ name="MissingAlert" label="Unknown Alert Message" type="alertmodal"> -Your version of Second Life does not know how to display the alert it just received. Please verify that you have the latest Viewer installed. +Your version of [APP_NAME] does not know how to display the alert it just received. Please verify that you have the latest Viewer installed. Error details: The alert called '[_NAME]' was not found in notifications.xml. <usetemplate @@ -154,7 +154,7 @@ No tutorial is currently available. icon="alertmodal.tga" name="BadInstallation" type="alertmodal"> - An error occurred while updating Second Life. Please download the latest version of the Viewer. http://get.secondlife.com + An error occurred while updating [APP_NAME]. Please download the latest version of the Viewer. http://get.secondlife.com <usetemplate name="okbutton" yestext="Ok"/> @@ -164,7 +164,7 @@ No tutorial is currently available. icon="alertmodal.tga" name="LoginFailedNoNetwork" type="alertmodal"> -Could not connect to the Second Life Grid. +Could not connect to the [SECOND_LIFE_GRID]. '[DIAGNOSTIC]' Make sure your Internet connection is working properly. <usetemplate @@ -331,7 +331,7 @@ Members cannot be removed from that role. The members must resign from the role themselves. Are you sure you want to continue? <usetemplate - ignoretext="When adding group members to the owner role" + ignoretext="Confirm before I add a new group Owner" name="okcancelignore" notext="No" yestext="Yes"/> @@ -429,7 +429,7 @@ Selecting "Show in Search" will show: type="alertmodal"> You can propose to another Resident or dissolve an existing partnership through the [SECOND_LIFE] website. -Go to the Second Life web site for more information on partnering? +Go to the [SECOND_LIFE] web site for more information on partnering? <usetemplate name="okcancelbuttons" notext="Cancel" @@ -519,7 +519,7 @@ Your selling price will be L$[SALE_PRICE] and will be authorized for sale to [NA icon="alertmodal.tga" name="ConfirmLandSaleToAnyoneChange" type="alertmodal"> -ATTENTION: Clicking 'sell to anyone' makes your land available to the entire Second Life community, even those not in this region. +ATTENTION: Clicking 'sell to anyone' makes your land available to the entire [SECOND_LIFE] community, even those not in this region. The selected [LAND_SIZE] m² land is being set for sale. Your selling price will be L$[SALE_PRICE] and will be authorized for sale to [NAME]. @@ -745,6 +745,7 @@ You need to enter both the First and Last name of your avatar. You need an account to enter [SECOND_LIFE]. Would you like to create one now? <url option="0" + name="url" openexternally = "1"> http://secondlife.com/registration/ @@ -764,7 +765,7 @@ Fill out your ad, then click 'Publish...' to add it to the directory. You'll be asked for a price to pay when clicking Publish. Paying more makes your ad appear higher in the list, and also appear higher when people search for keywords. <usetemplate - ignoretext="When adding a new Classified" + ignoretext="How to create a new Classified ad" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -810,7 +811,7 @@ Delete pick [PICK]? name="PromptGoToEventsPage" type="alertmodal"> Go to the [SECOND_LIFE] events web page? - <url option="0"> + <url option="0" name="url"> http://secondlife.com/events/ </url> @@ -860,14 +861,14 @@ Would you like to disable all popups which can be skipped? icon="alertmodal.tga" name="CacheWillClear" type="alertmodal"> -Cache will be cleared after you restart [SECOND_LIFE]. +Cache will be cleared after you restart [APP_NAME]. </notification> <notification icon="alertmodal.tga" name="CacheWillBeMoved" type="alertmodal"> -Cache will be moved after you restart [SECOND_LIFE]. +Cache will be moved after you restart [APP_NAME]. Note: This will clear the cache. </notification> @@ -875,14 +876,14 @@ Note: This will clear the cache. icon="alertmodal.tga" name="ChangeConnectionPort" type="alertmodal"> -Port settings take effect after you restart [SECOND_LIFE]. +Port settings take effect after you restart [APP_NAME]. </notification> <notification icon="alertmodal.tga" name="ChangeSkin" type="alertmodal"> -The new skin will appear after you restart [SECOND_LIFE]. +The new skin will appear after you restart [APP_NAME]. </notification> <notification @@ -890,7 +891,7 @@ The new skin will appear after you restart [SECOND_LIFE]. name="GoToAuctionPage" type="alertmodal"> Go to the [SECOND_LIFE] web page to see auction details or make a bid? - <url option="0"> + <url option="0" name="url"> http://secondlife.com/auctions/auction-detail.php?id=[AUCTION_ID] </url> @@ -1019,7 +1020,7 @@ Unable to write file [[FILE]] icon="alertmodal.tga" name="UnsupportedHardware" type="alertmodal"> -Warning: Your system does not meet Second Life's minimum system requirements. If you continue using Second Life, you may experience poor performance. Unfortunately, we cannot provide technical support for unsupported system configurations. +Warning: Your system does not meet [APP_NAME]'s minimum system requirements. If you continue using [APP_NAME], you may experience poor performance. Unfortunately, the [SUPPORT_SITE] cannot provide technical support for unsupported system configurations. MINSPECS Do you wish to visit [_URL] for more information? @@ -1028,7 +1029,7 @@ Do you wish to visit [_URL] for more information? http://www.secondlife.com/corporate/sysreqs.php </url> <usetemplate - ignoretext="When detecting unsupported hardware" + ignoretext="My computer hardware is not supported" name="okcancelignore" notext="No" yestext="Yes"/> @@ -1038,12 +1039,12 @@ Do you wish to visit [_URL] for more information? icon="alertmodal.tga" name="UnknownGPU" type="alertmodal"> -Your system contains a graphics card that is unknown to us at this time. -This is often the case with new hardware we haven't had a chance to test. Second Life will most likely run properly, but you may need to adjust your graphics settings to something more appropriate. +Your system contains a graphics card that is unknown to [APP_NAME] at this time. +This is often the case with new hardware that hasn't been tested yet with [APP_NAME]. [APP_NAME] will most likely run properly, but you may need to adjust your graphics settings to something more appropriate. (Edit menu > Preferences > Graphics). <form name="form"> <ignore name="ignore" - text="When detecting an unknown graphics card"/> + text="My graphics card could not be identified"/> </form> </notification> @@ -1051,9 +1052,8 @@ This is often the case with new hardware we haven't had a chance to test. icon="alertmodal.tga" name="DisplaySettingsNoShaders" type="alertmodal"> -[SECOND_LIFE] crashed while initializing graphics drivers. -Graphics Quality will be set to low to avoid some common driver errors. -This will disable some graphics features. +[APP_NAME] crashed while initializing graphics drivers. +Graphics Quality will be set to Low to avoid some common driver errors. This will disable some graphics features. We recommend updating your graphics card drivers. Graphics Quality can be raised in Preferences > Graphics. </notification> @@ -1205,7 +1205,7 @@ Please move all objects to be acquired onto the same region. [EXTRA] Go to [_URL] for information on purchasing currency? - <url option="0"> + <url option="0" name="url"> http://secondlife.com/app/currency/ </url> @@ -1387,7 +1387,7 @@ Unable to create output file: [FILE] icon="alertmodal.tga" name="DoNotSupportBulkAnimationUpload" type="alertmodal"> -We do not currently support bulk upload of animation files. +[APP_NAME] does not currently support bulk upload of animation files. </notification> <notification @@ -1506,15 +1506,15 @@ Could not teleport. icon="alertmodal.tga" name="invalid_tport" type="alertmodal"> -Problem encountered processing your teleport request. You may need to log back in before you can teleport. If you continue to get this message, please check the Tech Support FAQ at: -www.secondlife.com/support +Problem encountered processing your teleport request. You may need to log back in before you can teleport. +If you continue to get this message, please check the [SUPPORT_SITE]. </notification> <notification icon="alertmodal.tga" name="invalid_region_handoff" type="alertmodal"> -Problem encountered processing your region crossing. You may need to log back in before you can cross regions. If you continue to get this message, please check the Tech Support FAQ at: -www.secondlife.com/support +Problem encountered processing your region crossing. You may need to log back in before you can cross regions. +If you continue to get this message, please check the [SUPPORT_SITE]. </notification> <notification icon="alertmodal.tga" @@ -1655,7 +1655,7 @@ Cannot find the region this land is in. icon="alertmodal.tga" name="CannotCloseFloaterBuyLand" type="alertmodal"> -You cannot close the Buy Land window until Second Life estimates the price of this transaction. +You cannot close the Buy Land window until [APP_NAME] estimates the price of this transaction. </notification> <notification @@ -1973,7 +1973,7 @@ and provide details about your network setup. type="alertmodal"> You have been logged out of [SECOND_LIFE]: [MESSAGE] -You can still look at existing IM and chat by clicking 'View IM & Chat'. Otherwise, click 'Quit' to exit [SECOND_LIFE] immediately. +You can still look at existing IM and chat by clicking 'View IM & Chat'. Otherwise, click 'Quit' to exit [APP_NAME] immediately. <usetemplate name="okcancelbuttons" notext="Quit" @@ -2331,10 +2331,10 @@ You may want to set a new home location. name="ClothingLoading" type="alertmodal"> Your clothing is still downloading. -You can use [SECOND_LIFE] normally and other users will see you correctly. +You can use [SECOND_LIFE] normally and other people will see you correctly. <form name="form"> <ignore name="ignore" - text="When clothing is taking a long time to download"/> + text="Clothing is taking a long time to download"/> </form> </notification> @@ -2343,7 +2343,7 @@ You can use [SECOND_LIFE] normally and other users will see you correctly. name="FirstRun" type="alertmodal"> -[SECOND_LIFE] installation is complete. +[APP_NAME] installation is complete. If this is your first time using [SECOND_LIFE], you will need to create an account before you can log in. Return to www.secondlife.com to create a new account? @@ -2357,10 +2357,10 @@ Return to www.secondlife.com to create a new account? icon="alertmodal.tga" name="LoginPacketNeverReceived" type="alertmodal"> -We're having trouble connecting. There may be a problem with your internet connection or the Second Life servers. +We're having trouble connecting. There may be a problem with your Internet connection or the [SECOND_LIFE_GRID]. -You can either check your internet connection and try again in a few minutes, click Help to connect to our support site, or click Teleport to attempt to teleport home. - <url option="1"> +You can either check your Internet connection and try again in a few minutes, click Help to view the [SUPPORT_SITE], or click Teleport to attempt to teleport home. + <url option="1" name="url"> http://secondlife.com/support/ </url> @@ -2590,6 +2590,45 @@ Finished download of raw terrain file to: icon="alertmodal.tga" name="DownloadWindowsMandatory" type="alertmodal"> +A new version of [APP_NAME] is available. +[MESSAGE] +You must download this update to use [APP_NAME]. + <usetemplate + name="okcancelbuttons" + notext="Quit" + yestext="Download"/> + </notification> + + <notification + icon="alertmodal.tga" + name="DownloadWindows" + type="alertmodal"> +An updated version of [APP_NAME] 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="DownloadWindowsReleaseForDownload" + type="alertmodal"> +An updated version of [APP_NAME] 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="DownloadLinuxMandatory" + type="alertmodal"> A new version of [SECOND_LIFE] is available. [MESSAGE] You must download this update to use [SECOND_LIFE]. @@ -2601,7 +2640,7 @@ You must download this update to use [SECOND_LIFE]. <notification icon="alertmodal.tga" - name="DownloadWindows" + name="DownloadLinux" type="alertmodal"> An updated version of [SECOND_LIFE] is available. [MESSAGE] @@ -2614,7 +2653,7 @@ This update is not required, but we suggest you install it to improve performanc <notification icon="alertmodal.tga" - name="DownloadWindowsReleaseForDownload" + name="DownloadLinuxReleaseForDownload" type="alertmodal"> An updated version of [SECOND_LIFE] is available. [MESSAGE] @@ -2629,9 +2668,9 @@ This update is not required, but we suggest you install it to improve performanc icon="alertmodal.tga" name="DownloadMacMandatory" type="alertmodal"> -A new version of [SECOND_LIFE] is available. +A new version of [APP_NAME] is available. [MESSAGE] -You must download this update to use [SECOND_LIFE]. +You must download this update to use [APP_NAME]. Download to your Applications folder? <usetemplate @@ -2644,7 +2683,7 @@ Download to your Applications folder? icon="alertmodal.tga" name="DownloadMac" type="alertmodal"> -An updated version of [SECOND_LIFE] is available. +An updated version of [APP_NAME] is available. [MESSAGE] This update is not required, but we suggest you install it to improve performance and stability. @@ -2659,7 +2698,7 @@ Download to your Applications folder? icon="alertmodal.tga" name="DownloadMacReleaseForDownload" type="alertmodal"> -An updated version of [SECOND_LIFE] is available. +An updated version of [APP_NAME] is available. [MESSAGE] This update is not required, but we suggest you install it to improve performance and stability. @@ -2677,7 +2716,7 @@ Download to your Applications folder? Deeding this object will cause the group to: * Receive L$ paid into the object <usetemplate - ignoretext="When deeding objects to groups" + ignoretext="Confirm before I deed an object to a group" name="okcancelignore" notext="Cancel" yestext="Deed"/> @@ -2687,9 +2726,9 @@ Deeding this object will cause the group to: icon="alertmodal.tga" name="WebLaunchExternalTarget" type="alertmodal"> -Open your system Web browser to view this content? +Do you want to open your Web browser to view this content? <usetemplate - ignoretext="When opening your system browser to view a Web page" + ignoretext="Launch my browser to view a web page" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2699,9 +2738,9 @@ Open your system Web browser to view this content? icon="alertmodal.tga" name="WebLaunchJoinNow" type="alertmodal"> -Go to www.secondlife.com to manage your account? +Go to secondlife.com to manage your account? <usetemplate - ignoretext="When launching web browser to manage your account" + ignoretext="Launch my browser to manage my account" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2713,7 +2752,7 @@ Go to www.secondlife.com to manage your account? type="alertmodal"> Visit the [SECOND_LIFE] Wiki and learn how to report bugs correctly. <usetemplate - ignoretext="When launching web browser to view the Bug Reporting 101 Wiki" + ignoretext="Launch my browser to learn how to report a Bug" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2725,7 +2764,7 @@ Visit the [SECOND_LIFE] Wiki and learn how to report bugs correctly. type="alertmodal"> Visit the [SECOND_LIFE] Wiki for details of how to report a security issue. <usetemplate - ignoretext="When launching web browser to view Security Issues Wiki" + ignoretext="Launch my browser to learn how to report a Security Issue" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2737,7 +2776,7 @@ Visit the [SECOND_LIFE] Wiki for details of how to report a security issue. type="alertmodal"> Visit the [SECOND_LIFE] QA Wiki. <usetemplate - ignoretext="When launching web browser to view the QA Wiki" + ignoretext="Launch my browser to view the QA Wiki" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2749,7 +2788,7 @@ Visit the [SECOND_LIFE] QA Wiki. type="alertmodal"> Visit the [SECOND_LIFE] Public Issue Tracker, where you can report bugs and other issues. <usetemplate - ignoretext="When launching web browser to view the Public Issue Tracker" + ignoretext="Launch my browser to use the Public Issue Tracker" name="okcancelignore" notext="Cancel" yestext="Go to page"/> @@ -2761,7 +2800,7 @@ Visit the [SECOND_LIFE] Public Issue Tracker, where you can report bugs and othe type="alertmodal"> Visit the [SECOND_LIFE] Wiki for info on how to use the Public Issue Tracker. <usetemplate - ignoretext="When launching web browser to view Public Issue Tracker Wiki" + ignoretext="Launch my browser to view instructions for the Public Issue Tracker" name="okcancelignore" notext="Cancel" yestext="Go to page"/> @@ -2773,7 +2812,7 @@ Visit the [SECOND_LIFE] Wiki for info on how to use the Public Issue Tracker. type="alertmodal"> Go to the Official Linden Blog, for the latest news and information. <usetemplate - ignoretext="When launching web browser to view the blog" + ignoretext="Launch my browser to view the blog" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2783,9 +2822,9 @@ Go to the Official Linden Blog, for the latest news and information. icon="alertmodal.tga" name="WebLaunchLSLGuide" type="alertmodal"> -Go to the Scripting Guide for scripting help? +Do you want to open the Scripting Guide for help with scripting? <usetemplate - ignoretext="When launching web browser to view the Scripting Guide" + ignoretext="Launch my browser to view the Scripting Guide" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2795,9 +2834,9 @@ Go to the Scripting Guide for scripting help? icon="alertmodal.tga" name="WebLaunchLSLWiki" type="alertmodal"> -Go to the LSL Portal for scripting help? +Do you want to visit the LSL Portal for help with scripting? <usetemplate - ignoretext="When launching web browser to view the LSL Portal" + ignoretext="Launch my browser to view the LSL Portal" name="okcancelignore" notext="Cancel" yestext="Go to page"/> @@ -2811,7 +2850,7 @@ Are you sure you want to return the selected objects to their owners? Transferab *WARNING* No-transfer deeded objects will be deleted! <usetemplate - ignoretext="When returning objects to their owners" + ignoretext="Confirm before I return objects to their owners" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -2903,10 +2942,10 @@ Cannot offer friendship at this time. Please try again in a moment. icon="alert.tga" name="BusyModeSet" type="alert"> -Busy mode set. +Busy mode is set. Chat and instant messages will be hidden. Instant messages will get your Busy mode response. All teleportation offers will be declined. All inventory offers will go to your Trash. <usetemplate - ignoretext="When setting busy mode" + ignoretext="I change my status to Busy mode" name="okignore" yestext="OK"/> </notification> @@ -3056,7 +3095,7 @@ Join me in [REGION] type="alertmodal"> Are you sure you want to teleport? <usetemplate - ignoretext="When teleporting from a landmark in inventory" + ignoretext="Confirm that I want to teleport to a landmark" name="okcancelignore" notext="Cancel" yestext="Teleport"/> @@ -3068,7 +3107,7 @@ Are you sure you want to teleport? type="alertmodal"> Teleport to [PICK]? <usetemplate - ignoretext="When teleporting from double-clicking a pick" + ignoretext="Confirm that I want to teleport to a location in Picks" name="okcancelignore" notext="Cancel" yestext="Teleport"/> @@ -3278,7 +3317,7 @@ Go to the Knowledge Base for more information about maturity Ratings? name="okcancelignore" yestext="Go to Knowledge Base" notext="Close" - ignoretext="When Region entry is blocked due to maturity Rating"/> + ignoretext="I can't enter this Region, due to restrictions of the maturity Rating"/> </notification> <notification @@ -3305,7 +3344,7 @@ You can click 'Change Preference' to raise your maturity Rating prefer index="1" name="Cancel" text="Close"/> - <ignore name="ignore" text="When Region entry is blocked due to maturity Rating preference"/> + <ignore name="ignore" text="My chosen Rating preference prevents me from entering a Region"/> </form> </notification> @@ -3335,7 +3374,7 @@ Go to the Knowledge Base for more information about maturity Ratings? name="okcancelignore" yestext="Go to Knowledge Base" notext="Close" - ignoretext="When claiming land is blocked due to maturity Rating"/> + ignoretext="I can't claim this Land, due to restrictions of the maturity Rating"/> </notification> <notification @@ -3356,7 +3395,7 @@ You can click 'Change Preference' to raise your maturity Rating prefer name="okcancelignore" yestext="Change Preference" notext="Close" - ignoretext="When claiming land is blocked due to maturity Rating preference"/> + ignoretext="My chosen Rating preference prevents me from claiming Land"/> </notification> <notification @@ -3385,7 +3424,7 @@ Go to the Knowledge Base for more information about maturity Ratings? name="okcancelignore" yestext="Go to Knowledge Base" notext="Close" - ignoretext="When a Land purchase is blocked due to maturity Rating"/> + ignoretext="I can't buy this Land, due to restrictions of the maturity Rating"/> </notification> <notification @@ -3406,7 +3445,7 @@ You can click 'Change Preference' to raise your maturity Rating prefer name="okcancelignore" yestext="Change Preference" notext="Close" - ignoretext="When a Land purchase is blocked due to maturity Rating preference"/> + ignoretext="My chosen Rating preference prevents me from buying Land"/> </notification> <notification @@ -3929,7 +3968,7 @@ Default: off label="Voice Version Mismatch" name="VoiceVersionMismatch" type="alertmodal"> -This version of Second Life is not compatible with the Voice Chat feature in this region. In order for Voice Chat to function correctly you will need to update Second Life. +This version of [APP_NAME] is not compatible with the Voice Chat feature in this region. In order for Voice Chat to function correctly you will need to update [APP_NAME]. </notification> <notification @@ -4118,7 +4157,7 @@ These items will be moved to your inventory, not copied. Move the inventory item(s)? <usetemplate - ignoretext="When moving no-copy inventory from objects" + ignoretext="Warn me before I move 'no-copy' items from an object" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -4133,7 +4172,7 @@ Because this object is scripted, moving these items to your inventory may cause Move the inventory item(s)? <usetemplate - ignoretext="When moving no-copy inventory from scripted objects" + ignoretext="Warn me before I move 'no-copy' items which might break a scripted object" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -4143,10 +4182,10 @@ Move the inventory item(s)? icon="alert.tga" name="ClickActionNotPayable" type="alert"> -Warning: The Pay Object click action has been set, but it will only work if a script is added with a money() event. +Warning: The 'Pay object' click action has been set, but it will only work if a script is added with a money() event. <form name="form"> <ignore name="ignore" - text="When Setting 'Pay' on objects without money() events"/> + text="I set the action 'Pay object' when building an object without a money() script"/> </form> </notification> @@ -4161,9 +4200,9 @@ There are no items in this object that you are allowed to copy. icon="alertmodal.tga" name="WebLaunchAccountHistory" type="alertmodal"> -Go to the Second Life web site to see your account history? +Go to secondlife.com to see your account history? <usetemplate - ignoretext="When loading account history web page" + ignoretext="Launch my browser to see my account history" name="okcancelignore" notext="Cancel" yestext="Go to page"/> @@ -4173,9 +4212,9 @@ Go to the Second Life web site to see your account history? icon="alertmodal.tga" name="ClickOpenF1Help" type="alertmodal"> -View Second Life help? +Do you want to visit [SECOND_LIFE] help? <usetemplate - ignoretext="When visiting the Second Life Support Website." + ignoretext="Launch my browser to view Help/Support" name="okcancelignore" notext="Cancel" yestext="Go"/> @@ -4187,7 +4226,7 @@ View Second Life help? type="alertmodal"> Are you sure you want to quit? <usetemplate - ignoretext="When Quitting Second Life." + ignoretext="Confirm before I quit" name="okcancelignore" notext="Don't Quit" yestext="Quit"/> @@ -4219,7 +4258,7 @@ As a service to residents and visitors, the owner of the region you are in has e The region owner will resolve reports based on the local rules of this region as outlined in the estate Covenant. (View covenants by going to the World menu and selecting About Land.) -The resolution of this report applies only to this Region; Residents access to other areas of Second Life will not be affected by the outcome of this report. Only Linden Lab can restrict access to the entirety of Second Life. +The resolution of this report applies only to this Region. Residents' access to other areas of [SECOND_LIFE] will not be affected by the outcome of this report. Only Linden Lab can restrict access to the entirety of [SECOND_LIFE]. </notification> <notification @@ -4312,9 +4351,9 @@ Dear Resident, You appear to be reporting intellectual property infringement. Please make sure you are reporting it correctly: -(1) The Abuse Process. You may submit an abuse report if you believe a Resident is exploiting the Second Life permissions system, for example, by using CopyBot or similar copying tools, to infringe intellectual property rights. The Abuse Team investigates and issues appropriate disciplinary action for behavior that violates the Second Life Community Standards or Terms of Service. However, the Abuse Team does not handle and will not respond to requests to remove content from the Second Life world. +(1) The Abuse Process. You may submit an abuse report if you believe a Resident is exploiting the [SECOND_LIFE] permissions system, for example, by using CopyBot or similar copying tools, to infringe intellectual property rights. The Abuse Team investigates and issues appropriate disciplinary action for behavior that violates the [SECOND_LIFE] Community Standards or Terms of Service. However, the Abuse Team does not handle and will not respond to requests to remove content from the [SECOND_LIFE] world. -(2) The DMCA or Content Removal Process. To request removal of content from Second Life, you MUST submit a valid notification of infringement as provided in our DMCA Policy at http://secondlife.com/corporate/dmca.php. +(2) The DMCA or Content Removal Process. To request removal of content from [SECOND_LIFE], you MUST submit a valid notification of infringement as provided in our DMCA Policy at http://secondlife.com/corporate/dmca.php. If you still wish to continue with the abuse process, please close this window and finish submitting your report. You may need to select the specific category 'CopyBot or Permissions Exploit'. @@ -4341,7 +4380,7 @@ Do you want to replace it with the selected object? <form name="form"> <ignore name="ignore" save_option="true" - text="When replacing existing attachments"/> + text="Replace an existing attachment with the selected item"/> <button default="true" ignore="Replace Automatically" @@ -4367,7 +4406,7 @@ Would you like to leave Busy Mode before completing this transaction? <form name="form"> <ignore name="ignore" save_option="true" - text="When paying a person or object in busy mode"/> + text="I am about to pay a person or object while I am in Busy mode"/> <button default="true" ignore="Always leave Busy Mode" @@ -4388,7 +4427,7 @@ Would you like to leave Busy Mode before completing this transaction? type="alertmodal"> Are you sure you want to permanently delete the contents of your Trash? <usetemplate - ignoretext="When emptying your inventory trash folder" + ignoretext="Confirm before I empty the inventory Trash folder" name="okcancelignore" notext="Cancel" yestext="OK"/> @@ -4398,7 +4437,7 @@ Are you sure you want to permanently delete the contents of your Trash? icon="alertmodal.tga" name="ConfirmClearBrowserCache" type="alertmodal"> -Are you sure you want to clear your browser cache? +Delete your travel, web, and search history? <usetemplate name="okcancelbuttons" notext="Cancel" @@ -4433,7 +4472,7 @@ Are you sure you want to clear your list of saved URLs? type="alertmodal"> Are you sure you want to permanently delete the contents of your Lost And Found? <usetemplate - ignoretext="When emptying your inventory Lost And Found folder" + ignoretext="Confirm before I empty the inventory Lost And Found folder" name="okcancelignore" notext="No" yestext="Yes"/> @@ -4446,10 +4485,10 @@ Are you sure you want to permanently delete the contents of your Lost And Found? The following SLurl has been copied to your clipboard: [SLURL] -Put it in a web page to give others easy access to this location or try it out yourself by pasting it into the address bar of your web browser. +Link to this from a web page to give others easy access to this location, or try it out yourself by pasting it into the address bar of any web browser. <form name="form"> <ignore name="ignore" - text="When copying a SLurl to your clipboard"/> + text="SLurl is copied to my clipboard"/> </form> </notification> @@ -4564,7 +4603,7 @@ Click "Advanced Water" to bring up an editor with more advanced settin icon="alertmodal.tga" name="HelpDayCycle" type="alertmodal"> -The Day Cycle Editor gives you control over the sky during Second Life's day/night cycle. This is the cycle that is used by the Basic Environment Editor's Time of Day slider. +The Day Cycle Editor gives you control over the sky during [SECOND_LIFE]'s day/night cycle. This is the cycle that is used by the Basic Environment Editor's Time of Day slider. The Day Cycle Editor works by setting keyframes. These are nodes (represented by the gray blips on the time graph) that have Sky Presets associated with them. As the Time of Day progresses, the WindLight sky "animates" as it interpolates between these keyframes. @@ -4731,7 +4770,7 @@ Controls the speed of the clouds as they move in the Y direction. icon="alertmodal.tga" name="HelpClassicClouds" type="alertmodal"> -Check this box to enable rendering of Second Life's older classic clouds in addition to WindLight's clouds. +Check this box to enable rendering of [SECOND_LIFE]'s older classic clouds in addition to WindLight's clouds. </notification> <notification @@ -4943,9 +4982,9 @@ Granting this request gives a script ongoing permission to take Linden dollars ( icon="alertmodal.tga" name="AutoWearNewClothing" type="alertmodal"> -Automatically wear the clothing you create? +Would you like to automatically wear the clothing you are about to create? <usetemplate - ignoretext="Automatically wear new clothing" + ignoretext="Wear the clothing I create while editing My Appearance" name="okcancelignore" notext="No" yestext="Yes"/> @@ -4955,15 +4994,15 @@ Automatically wear the clothing you create? icon="alertmodal.tga" name="NotAgeVerified" type="alertmodal"> -You must be age-verified to visit this area. Visit the Second Life website and verify your age? +You must be age-verified to visit this area. Do you want to go to the [SECOND_LIFE] website and verify your age? [_URL] - <url option="0"> + <url option="0" name="url"> https://secondlife.com/account/verification.php </url> <usetemplate - ignoretext="Warn about lack of age verification" + ignoretext="I have not verified my age" name="okcancelignore" notext="No" yestext="Yes"/> @@ -4973,7 +5012,7 @@ You must be age-verified to visit this area. Visit the Second Life website and icon="alertmodal.tga" name="Cannot enter parcel: no payment info on file" type="alertmodal"> -You must have payment information on file to visit this area. Visit the Second Life website and set this up? +You must have payment information on file to visit this area. Do you want to go to the [SECOND_LIFE] website and set this up? [_URL] <url option="0" name="url"> @@ -4981,7 +5020,7 @@ You must have payment information on file to visit this area. Visit the Second https://secondlife.com/account/ </url> <usetemplate - ignoretext="Warn about lack of payment info" + ignoretext="I lack payment information on file" name="okcancelignore" notext="No" yestext="Yes"/> @@ -5412,7 +5451,25 @@ How odd. The string [STRING_NAME] is missing from strings.xml Apple's QuickTime software does not appear to be installed on your system. If you want to view streaming media on parcels that support it you should go to the QuickTime site (http://www.apple.com/quicktime) and install the QuickTime Player. </notification> + <notification + icon="notify.tga" + name="NoPlugin" + type="notify"> + No Media Plugin was found to handle the "[MIME_TYPE]" mime type. Media of this type will be unavailable. + </notification> + <notification + icon="alertmodal.tga" + name="MediaPluginFailed" + type="alertmodal"> + The following Media Plugin has failed: + [PLUGIN] +Please re-install the plugin or contact the vendor if you continue to experience problems. + <form name="form"> + <ignore name="ignore" + text="When a Media Plugin fails"/> + </form> + </notification> <notification icon="notify.tga" name="OwnedObjectsReturned" @@ -5733,7 +5790,7 @@ How odd. The string [STRING_NAME] is missing from strings.xml icon="notify.tga" name="ObjectGiveItemUnknownUser" type="notify"> - An object named [OBJECTFROMNAME] owned by (an unknown user) has given you a [OBJECTTYPE] named [OBJECTNAME]. + An object named [OBJECTFROMNAME] owned by (an unknown Resident) has given you a [OBJECTTYPE] named [OBJECTNAME]. <form name="form"> <button index="0" @@ -5968,7 +6025,7 @@ How odd. The string [STRING_NAME] is missing from strings.xml icon="notify.tga" name="InvalidWearable" type="notify"> - The item you are trying to wear uses a feature that your viewer can't read. Please upgrade your version of Second Life to wear this item. + The item you are trying to wear uses a feature that your Viewer can't read. Please upgrade your version of [APP_NAME] to wear this item. </notification> <notification @@ -6106,7 +6163,7 @@ How odd. The string [STRING_NAME] is missing from strings.xml icon="notify.tga" name="FirstTeleport" type="notify"> - You can only teleport to certain areas in this region. The arrow points to your specific destination. Click to dismiss it. + You can only teleport to certain areas in this region. The arrow points to your specific destination. Click the arrow to dismiss it. </notification> <notification @@ -6145,7 +6202,7 @@ When you are done, press 'Save All'. type="notify"> This is a sandbox area, and is meant to help Residents learn how to build. - Things you build here will be deleted after you leave, so don't forget to right-click and choose 'take' to move your creation to your Inventory. + Things you build here will be deleted after you leave, so don't forget to right-click and choose 'Take' to move your creation to your Inventory. </notification> <notification @@ -6174,12 +6231,12 @@ When you are done, press 'Save All'. You are editing a Sculpted prim. Sculpties require a special texture to define their shape. </notification> - <notification + <!--notification icon="notify.tga" name="FirstMedia" type="notify"> You have begun playing media. Media can set to play automatically in the preferences window under Audio / Video. Note that this can be a security risk for media sites you do not trust. - </notification> + </notification--> <notification icon="notifytip.tga" @@ -6265,7 +6322,7 @@ Click Accept to join the call or Decline to decline the invitation. Click Block name="VoiceInviteAdHoc" type="notify"> [NAME] has joined a voice chat call with a conference chat. -Click Accept to join the call or Decline to decline the invitation. Click Block to block this user. +Click Accept to join the call or Decline to decline the invitation. Click Block to block this caller. <unique> <context key="NAME"/> </unique> @@ -6290,7 +6347,7 @@ Click Accept to join the call or Decline to decline the invitation. Click Block name="InviteAdHoc" type="notify"> [NAME] is inviting you to a conference chat. -Click Accept to join the chat or Decline to decline the invitation. Click Block to block this user. +Click Accept to join the chat or Decline to decline the invitation. Click Block to block this caller. <unique> <context key="NAME"/> </unique> @@ -6476,9 +6533,9 @@ Click Accept to join the chat or Decline to decline the invitation. Click Block </global> <global name="UnsupportedGLRequirements"> -You do not appear to have the proper hardware requirements for Second Life. Second Life requires an OpenGL graphics card that has multitexture support. If this is the case, you may want to make sure that you have the latest drivers for your graphics card, and service packs and patches for your operating system. +You do not appear to have the proper hardware requirements for [APP_NAME]. [APP_NAME] requires an OpenGL graphics card that has multitexture support. If this is the case, you may want to make sure that you have the latest drivers for your graphics card, and service packs and patches for your operating system. -If you continue to have problems, please visit: http://www.secondlife.com/support +If you continue to have problems, please visit the [SUPPORT_SITE]. </global> <global name="UnsupportedCPUAmount"> diff --git a/indra/newview/skins/default/xui/en/panel_activeim_row.xml b/indra/newview/skins/default/xui/en/panel_activeim_row.xml new file mode 100644 index 0000000000..8977f30a51 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_activeim_row.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="bottom|right" + name="panel_activeim_row" + layout="topleft" + top="0" + left="0" + height="40" + width="332" + background_visible="true" + bevel_style="in" + bg_alpha_color="0 0 0 0"> + <chiclet_im + name="chiclet" + layout="topleft" + top="4" + left="10" + height="26" + width="45"> + </chiclet_im> + <text + type="string" + name="contact_name" + layout="topleft" + top="8" + left="70" + height="20" + width="230" + length="1" + follows="left|top|right|bottom" + font="SansSerifBigBold" + text_color="White">Contact Name</text> + <button + name="hide_btn" + layout="topleft" + top="10" + left="310" + height="20" + width="20" + label="" + image_unselected="toast_hide_btn.tga" + image_disabled="toast_hide_btn.tga" + image_selected="toast_hide_btn.tga" + image_hover_selected="toast_hide_btn.tga" + image_disabled_selected="toast_hide_btn.tga"> + </button> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_bottomtray.xml b/indra/newview/skins/default/xui/en/panel_bottomtray.xml index 91039539f9..b6d0678cfb 100644 --- a/indra/newview/skins/default/xui/en/panel_bottomtray.xml +++ b/indra/newview/skins/default/xui/en/panel_bottomtray.xml @@ -31,8 +31,7 @@ layout="topleft" left="0" top="0" - width="5" - min_width="5" /> + width="5"/> <layout_panel follows="left|right" height="28" @@ -55,8 +54,7 @@ left="0" name="DUMMY" top="0" - width="3" - min_width="3"/> + width="3"/> <layout_panel auto_resize="false" follows="right" @@ -67,6 +65,7 @@ width="70" top_delta="-10" min_width="70" + name="movement_panel" user_resize="false"> <button follows="right" @@ -94,8 +93,7 @@ left="0" name="DUMMY" top="0" - width="8" - min_width="8"/> + width="8"/> <layout_panel auto_resize="false" follows="right" @@ -103,6 +101,7 @@ layout="topleft" min_height="28" min_width="150" + name="cam_panel" top_delta="-10" width="150"> <split_button @@ -116,6 +115,7 @@ image_unselected="camera_presets/camera_presets_arrow.png" image_disabled_selected="camera_presets/camera_presets_arrow.png" image_disabled="camera_presets/camera_presets_arrow.png" + name="camera_presets" tool_tip="Camera Presets" /> <split_button.item @@ -166,8 +166,8 @@ image_unselected="camera_presets/camera_presets_arrow_right.png" image_disabled_selected="camera_presets/camera_presets_arrow_right.png" image_disabled="camera_presets/camera_presets_arrow_right.png" + name="snapshot_settings" tool_tip="Snapshot Settings" /> - /> <split_button.item image_selected="camera_presets/camera_presets_snapshot.png" image_unselected="camera_presets/camera_presets_snapshot.png" @@ -185,8 +185,7 @@ left="0" name="DUMMY" top="0" - width="5" - min_width="5" /> + width="5"/> <layout_panel follows="left|right" height="28" @@ -216,8 +215,7 @@ layout="topleft" left="0" top="0" - width="5" - min_width="5" /> + width="5"/> <layout_panel auto_resize="false" follows="right" @@ -245,9 +243,11 @@ height="20" left="18" top="23"/> +<!-- <chiclet_notification.commit_callback function="Notification.Show" parameter="ClickUnimplemented" /> + --> </chiclet_notification> </layout_panel> <icon @@ -259,8 +259,7 @@ layout="topleft" left="0" top="0" - width="10" - min_width="10"/> + width="10"/> <view_border auto_resize="false" bevel_style="in" @@ -280,8 +279,7 @@ layout="topleft" left="0" top="0" - width="10" - min_width="10" /> + width="10"/> <layout_panel auto_resize="false" follows="right" @@ -325,7 +323,6 @@ layout="topleft" left="0" top="0" - width="5" - min_width="5" /> + width="5"/> </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_classified.xml b/indra/newview/skins/default/xui/en/panel_classified.xml index 3acd8fae4b..fde795260e 100644 --- a/indra/newview/skins/default/xui/en/panel_classified.xml +++ b/indra/newview/skins/default/xui/en/panel_classified.xml @@ -84,7 +84,7 @@ <button follows="left|top" height="20" - label="Show on Map" + label="Map" layout="topleft" left_pad="5" name="classified_map_btn" diff --git a/indra/newview/skins/default/xui/en/panel_edit_pick.xml b/indra/newview/skins/default/xui/en/panel_edit_pick.xml index a74f560753..28cb3bc551 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_pick.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_pick.xml @@ -19,7 +19,7 @@ left="10" name="title" text_color="white" - top="0" + top="17" width="250"> Edit Pick </text> @@ -28,11 +28,11 @@ bg_alpha_color="DkGray2" width="280" follows="left|right|top|bottom" - height="570" + height="560" layout="topleft" left="10" right="-10" - top="30"> + top_pad="5"> <panel follows="left|top|right" height="150" diff --git a/indra/newview/skins/default/xui/en/panel_edit_profile.xml b/indra/newview/skins/default/xui/en/panel_edit_profile.xml index b0f8052a9c..c870e24e59 100644 --- a/indra/newview/skins/default/xui/en/panel_edit_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_edit_profile.xml @@ -13,8 +13,8 @@ top="10" width="305"> <text - top="0" - left="9" + top="16" + left="11" height="20" follows="left|top" font="SansSerifHugeBold" @@ -28,7 +28,7 @@ background_visible="true" bg_alpha_color="DkGray2" follows="left|top|right|bottom" - height="620" + height="604" layout="topleft" left="9" name="data_panel" @@ -73,7 +73,7 @@ text_color="white" top="0" width="125"> - Second Life photo: + [SECOND_LIFE] photo: </text> <texture_picker allow_no_texture="true" @@ -149,7 +149,7 @@ text_color="white" top_pad="10" width="250"> - Second Life description: + [SECOND_LIFE] description: </text> <text_editor type="string" diff --git a/indra/newview/skins/default/xui/en/panel_group_control_panel.xml b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml new file mode 100644 index 0000000000..66a3a75cc4 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_group_control_panel.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<panel name="panel_im_control_panel"
+ width="96"
+ height="215"
+ border="false">
+ <avatar_list
+ width="90"
+ column_padding="0"
+ draw_heading="true"
+ draw_stripes="false"
+ follows="left|top|bottom|right"
+ layout="topleft"
+ left="3"
+ name="speakers_list"
+ search_column="1"
+ sort_column="2"
+ top="10"
+ height="150" />
+
+ <button name="group_info_btn"
+ label="Group Info"
+ left_delta="3"
+ width="90"
+ height="20" />
+
+ <button name="call_btn"
+ label="Call"
+ width="90"
+ height="20" />
+
+</panel>
diff --git a/indra/newview/skins/default/xui/en/panel_group_general.xml b/indra/newview/skins/default/xui/en/panel_group_general.xml index b9a384bf8f..2243df951e 100644 --- a/indra/newview/skins/default/xui/en/panel_group_general.xml +++ b/indra/newview/skins/default/xui/en/panel_group_general.xml @@ -2,13 +2,13 @@ <panel border="true" follows="all" - height="514" + height="470" label="General" class="panel_group_general" layout="topleft" left="1" name="general_tab" - top="514" + top="500" width="280"> <panel.string name="help_text"> @@ -40,137 +40,32 @@ Hover your mouse over the options for more help. name="help_button" top="8" width="20" /> --> - <line_editor - follows="left|top" - font="SansSerifSmall" - prevalidate_callback="asci" - halign="left" - height="16" - label="Type your new group name here" - layout="topleft" - left="10" - max_length="35" - name="group_name_editor" - top="8" - width="235" /> - <text - type="string" - length="1" - follows="left|top" - font="SansSerifSmall" - height="16" - layout="topleft" - left_delta="0" - name="group_name" - top_delta="0" - width="240"> - Type your new group name here - </text> - <text - type="string" - length="1" - follows="left|top" - height="16" - layout="topleft" - left_delta="0" - name="prepend_founded_by" - top_pad="4" - width="270"> - Founded by: - </text> - <name_box - follows="left|top" - height="16" - initial_value="(retrieving)" - layout="topleft" - left="30" - name="founder_name" - top_pad="0" - width="133" /> <text_editor type="string" - length="1" follows="left|top" - halign="left" - height="125" + left="95" + height="75" hide_scrollbar="true" layout="topleft" max_length="511" name="charter" - right="275" - top_pad="0" + top="30" width="170" word_wrap="true"> Group Charter </text_editor> - <texture_picker - follows="left|top" - height="96" - label="" - layout="topleft" - left_delta="-95" - name="insignia" - tool_tip="Click to choose a picture" - top_delta="1" - width="85" /> <text follows="left|top" type="string" - length="1" + font="SansSerifBig" + tool_tip="Owners are shown in bold." height="16" layout="topleft" - name="group_charter_label" - right="275" - top="190" - width="170"> - Group Charter - </text> - <button - follows="left|top" - height="22" - font="SansSerifSmall" - label="Join (L$0)" - label_selected="Join (L$0)" - layout="topleft" - left="10" - name="join_button" - top="160" - width="85" /> - <button - follows="left|top" - height="22" - font="SansSerifSmall" - label="Details" - label_selected="Detailed View" - layout="topleft" - left_delta="0" - name="info_button" - top_delta="0" - width="85" /> - <text - follows="left|top" - type="string" - length="1" - font="SansSerif" - height="16" - layout="topleft" - left_delta="0" + left="5" name="text_owners_and_visible_members" - top_pad="30" - width="270"> - Owners & Visible Members - </text> - <text - follows="left|top" - type="string" - length="1" - height="16" - layout="topleft" - left_delta="0" - name="text_owners_are_shown_in_bold" - top_pad="0" + top_pad="10" width="270"> - (Owners are shown in bold) + Members </text> <name_list column_padding="0" @@ -186,48 +81,73 @@ Hover your mouse over the options for more help. <name_list.columns label="Member Name" name="name" - relative_width="0.45" /> + relative_width="0.6" /> <name_list.columns label="Title" name="title" - relative_width="0.3" /> - <name_list.columns - label="Last Login" - name="online" - relative_width="0.25" /> - </name_list> + relative_width="0.4" /> + </name_list> <text follows="left|top" height="16" type="string" - length="1" top_pad="10" - font="SansSerif" + font="SansSerifBig" layout="topleft" name="text_group_preferences"> Group Preferences </text> - <panel - background_opaque="true" - bevel_style="in" - border="true" - follows="left|top" - height="125" - layout="topleft" - left_delta="0" - name="preferences_container" - top_pad="0" - width="263"> + <text + follows="left|top" + type="string" + height="16" + layout="topleft" + left_delta="0" + name="active_title_label" + top_pad="8" + width="240"> + My Active Title + </text> + <combo_box + follows="left|top" + height="20" + layout="topleft" + left_delta="0" + name="active_title" + tool_tip="Sets the title that appears in your avatar's name tag when this group is active." + top_pad="0" + width="240" /> <check_box height="16" - initial_value="true" - label="Show in search" + font="SansSerifSmall" + label="Receive notices" layout="topleft" - left="4" - name="show_in_group_list" - tool_tip="Let people see this group in search results." - top="4" - width="90" /> + left_delta="0" + name="receive_notices" + tool_tip="Sets whether you want to receive Notices from this group. Uncheck this box if this group is spamming you." + top_pad="5" + width="240" /> + <check_box + height="16" + label="Show in my profile" + layout="topleft" + left_delta="0" + name="list_groups_in_profile" + tool_tip="Sets whether you want to show this group in your Profile" + top_pad="5" + width="240" /> + <panel + background_visible="true" + bevel_style="in" + border="true" + bg_alpha_color="FloaterUnfocusBorderColor" + follows="left|top" + height="125" + layout="topleft" + left_delta="0" + name="preferences_container" + top_pad="10" + width="263"> <check_box follows="right|top" height="16" @@ -235,12 +155,12 @@ Hover your mouse over the options for more help. layout="topleft" left_delta="0" name="open_enrollement" - tool_tip="Sets whether this group allows new members to join without being invited." + tool_tip="Sets whether this group allows new members to join without being invited." top_pad="5" width="90" /> <check_box height="16" - label="Enrollment fee: L$" + label="Enrollment fee:" layout="topleft" left_delta="0" name="check_enrollment_fee" @@ -253,7 +173,8 @@ Hover your mouse over the options for more help. halign="left" height="16" increment="1" - label_width="10" + label_width="20" + label="L$" layout="topleft" left="25" max_val="99999" @@ -261,53 +182,25 @@ Hover your mouse over the options for more help. name="spin_enrollment_fee" tool_tip="New members must pay this fee to join the group when Enrollment Fee is checked." top_delta="-2" - width="65" /> - <check_box - height="16" - font="SansSerifSmall" - label="Receive notices" - layout="topleft" - left="5" - name="receive_notices" - tool_tip="Sets whether you want to receive Notices from this group. Uncheck this box if this group is spamming you." - top_pad="5" - width="240" /> + width="75" /> <check_box height="16" - label="List in my profile" - layout="topleft" - left="5" - name="list_groups_in_profile" - tool_tip="Sets whether you want to list this group in your Profile" - top_pad="5" - width="240" /> - <text - type="string" - length="1" - height="16" + initial_value="true" + label="Show in search" layout="topleft" - left="145" - name="active_title_label" - top="4" - width="95"> - My Active Title - </text> + left="4" + name="show_in_group_list" + tool_tip="Let people see this group in search results." + top_pad="4" + width="90" /> <combo_box height="20" layout="topleft" left_delta="0" - name="active_title" - tool_tip="Sets the title that appears in your avatar's name tag when this group is active." - top_pad="0" - width="110" /> - <combo_box - height="20" - layout="topleft" - left_delta="0" name="group_mature_check" tool_tip="Sets whether your group information is considered mature." top_pad="10" - width="120"> + width="240"> <combo_box.item label="Select Mature -" name="select_mature" diff --git a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml index 4f179d7a16..65d1e3c3a7 100644 --- a/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml +++ b/indra/newview/skins/default/xui/en/panel_group_info_sidetray.xml @@ -16,30 +16,60 @@ name="want_apply_text"> Do you want to apply these changes? </panel.string> - - <button + + <button layout="topleft" name="back" - left="5" - top="5" - width="20" - height="20" + right="-9" + top="0" + width="25" + height="25" label="" follows="top|left" - image_selected="navbar_bg_button.tga" - image_unselected="navbar_bg_button.tga" - image_overlay="navbar_back.tga"/> + image_overlay="BackArrow_Off" + tab_stop="false" /> <text layout="topleft" - top="5" - left_pad="15" - width="200" + top="0" + left="10" + width="250" height="20" - font="SansSerifBold" + font="SansSerifHugeBold" text_color="white" follows="top|left|right" mouse_opaque="true" name="group_name">(Loading...)</text> + <texture_picker + follows="left|top" + height="113" + label="" + layout="topleft" + left_delta="-10" + name="insignia" + tool_tip="Click to choose a picture" + top_pad="5" + width="100" /> + <text + type="string" + follows="left|top" + height="16" + length="1" + layout="topleft" + left_pad="10" + name="prepend_founded_by" + top_delta="0" + width="140"> + Founded by: + </text> + <name_box + follows="left|top" + height="16" + initial_value="(retrieving)" + layout="topleft" + left_delta="0" + name="founder_name" + top_pad="10" + width="140" /> <button top="632" height="20" @@ -53,7 +83,7 @@ top="632" left="75" height="20" - ont="SansSerifSmall" + font="SansSerifSmall" label="Refresh" label_selected="Refresh" name="btn_refresh" @@ -69,20 +99,20 @@ left="5" visible="false" width="65" /> - <accordion layout="topleft" left="2" width="296" top="28" height="600" follows="all" name="panel_me_profile"> + <accordion layout="topleft" left="2" width="296" top="135" height="600" follows="all" name="group_accordion"> <accordion_tab min_height="515" title="Group General" name="group_general_tab"> <panel class="panel_group_general" filename="panel_group_general.xml" name="group_general_tab_panel"/> </accordion_tab> - <accordion_tab min_height="380" title="Group Roles" name="group_roles_tab" can_resize="false"> + <accordion_tab min_height="380" title="Group Roles" name="group_roles_tab" expanded="False" can_resize="false"> <panel class="panel_group_roles" filename="panel_group_roles.xml" name="group_roles_tab_panel"/> </accordion_tab> - <accordion_tab min_height="530" title="Group Notices" name="group_notices_tab" can_resize="false"> + <accordion_tab min_height="530" title="Group Notices" name="group_notices_tab" expanded="False" can_resize="false"> <panel class="panel_group_notices" filename="panel_group_notices.xml" name="group_notices_tab_panel"/> </accordion_tab> - <accordion_tab min_height="270" title="Group Land Money" name="group_land_tab" can_resize="false"> + <accordion_tab min_height="270" title="Group Land Money" name="group_land_tab" expanded="False" can_resize="false"> <panel class="panel_group_land_money" filename="panel_group_land_money.xml" name="group_land_tab_panel"/> </accordion_tab> </accordion> -</panel> +</panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_group_invite.xml b/indra/newview/skins/default/xui/en/panel_group_invite.xml index 43457ddd61..68b30faa1c 100644 --- a/indra/newview/skins/default/xui/en/panel_group_invite.xml +++ b/indra/newview/skins/default/xui/en/panel_group_invite.xml @@ -15,6 +15,10 @@ name="loading"> (loading...) </panel.string> + <panel.string + name="already_in_group"> + Some avatars are already in group and were not invited. + </panel.string> <text type="string" length="1" diff --git a/indra/newview/skins/default/xui/en/panel_group_land_money.xml b/indra/newview/skins/default/xui/en/panel_group_land_money.xml index c52994b43d..069cf1d7bd 100644 --- a/indra/newview/skins/default/xui/en/panel_group_land_money.xml +++ b/indra/newview/skins/default/xui/en/panel_group_land_money.xml @@ -2,12 +2,12 @@ <panel border="true" follows="all" - height="420" + height="410" label="Land & L$" layout="topleft" left="1" name="land_money_tab" - top="470" + top="490" width="280"> <panel.string name="help_text"> @@ -40,9 +40,8 @@ top="8" width="20" /> --> - <text + <!-- <text type="string" - length="1" follows="left|top" font="SansSerifBig" height="16" @@ -52,7 +51,7 @@ top_pad="10" width="260"> Group Owned Land - </text> + </text> --> <scroll_list draw_heading="true" follows="top" @@ -60,9 +59,9 @@ heading_height="14" height="100" layout="topleft" - left_delta="0" + left="5" name="group_parcel_list" - top_pad="4" + top_pad="10" width="265"> <scroll_list.columns label="Parcel" @@ -88,8 +87,8 @@ <button follows="top" height="20" - label="Show on Map" - label_selected="Show on Map" + label="Map" + label_selected="Map" layout="topleft" left="150" name="map_button" @@ -98,7 +97,6 @@ enabled="false" /> <text type="string" - length="1" follows="left|top" halign="right" height="16" @@ -111,7 +109,6 @@ </text> <text type="string" - length="1" follows="left|top" height="16" layout="topleft" @@ -123,7 +120,6 @@ </text> <text type="string" - length="1" follows="left|top" halign="right" height="16" @@ -136,7 +132,6 @@ </text> <text type="string" - length="1" follows="left|top" height="16" layout="topleft" @@ -148,7 +143,6 @@ </text> <text type="string" - length="1" follows="left|top" halign="right" height="16" @@ -161,7 +155,6 @@ </text> <text type="string" - length="1" follows="left|top" height="16" layout="topleft" @@ -173,7 +166,6 @@ </text> <text type="string" - length="1" follows="left|top" halign="right" height="16" @@ -197,7 +189,6 @@ width="95" /> <text type="string" - length="1" follows="left|top" height="16" layout="topleft" @@ -208,14 +199,13 @@ </text> <text type="string" - length="1" follows="left|top" halign="right" height="16" layout="topleft" - left_pad="5" + left="140" name="your_contribution_max_value" - top_delta="0" + top_pad="0" width="95"> ([AMOUNT] max) </text> @@ -231,7 +221,6 @@ <text follows="left|top" type="string" - length="1" word_wrap="true" font="SansSerifSmall" height="40" @@ -245,7 +234,6 @@ </text> <text type="string" - length="1" follows="left|top" font="SansSerifBig" height="16" @@ -274,20 +262,19 @@ left="1" name="group_money_planning_tab" top="5" - width="270"> + width="265"> <text_editor type="string" - length="1" bg_readonly_color="0.784314 0.819608 0.8 1" follows="all" font="Monospace" - height="180" + height="172" layout="topleft" left="8" max_length="4096" name="group_money_planning_text" top="5" - width="260"> + width="250"> Computing... </text_editor> </panel> @@ -300,10 +287,9 @@ left_delta="0" name="group_money_details_tab" top_delta="0" - width="270"> + width="265"> <text_editor type="string" - length="1" bg_readonly_color="0.784314 0.819608 0.8 1" follows="all" font="Monospace" @@ -313,7 +299,7 @@ max_length="4096" name="group_money_details_text" top="7" - width="260"> + width="250"> Computing... </text_editor> <button @@ -346,10 +332,9 @@ left_delta="0" name="group_money_sales_tab" top_delta="-1" - width="270"> + width="265"> <text_editor type="string" - length="1" bg_readonly_color="0.784314 0.819608 0.8 1" follows="all" font="Monospace" @@ -359,7 +344,7 @@ max_length="4096" name="group_money_sales_text" top="7" - width="260"> + width="250"> Computing... </text_editor> <button @@ -384,4 +369,4 @@ width="125" /> </panel> </tab_container> -</panel>
\ No newline at end of file +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_group_notices.xml b/indra/newview/skins/default/xui/en/panel_group_notices.xml index 2e5f2dcb0b..ffc2f279bc 100644 --- a/indra/newview/skins/default/xui/en/panel_group_notices.xml +++ b/indra/newview/skins/default/xui/en/panel_group_notices.xml @@ -2,12 +2,12 @@ <panel border="true" follows="all" - height="530" + height="485" label="Notices" layout="topleft" left="1" name="notices_tab" - top="530" + top="485" width="280"> <panel.string name="help_text"> @@ -32,10 +32,9 @@ the General tab. name="help_button" top="8" width="20" /> --> - <text + <!--<text follows="left|top" type="string" - length="1" font="SansSerifBig" height="16" layout="topleft" @@ -44,19 +43,18 @@ the General tab. top_pad="10" width="269"> Group Notices Archive - </text> + </text> --> <text follows="left|top" type="string" - length="1" word_wrap="true" - height="73" + height="40" layout="topleft" - left_delta="0" + left_delta="10" name="lbl2" - top_pad="4" + top_pad="10" width="270"> - Notices are kept for 14 days. Click the notice below if you wish to view. Click the 'Refresh' button to check if new notices have been received. Notice lists are limited to 200 notices per group on a daily basis. + Notices are kept for 14 days. Notice lists are limited to 200 notices per group on a daily basis. </text> <scroll_list follows="left|top" @@ -92,7 +90,6 @@ the General tab. <text follows="left|top" type="string" - length="1" layout="topleft" name="notice_list_none_found" visible="false"> @@ -127,12 +124,11 @@ the General tab. layout="topleft" left="0" name="panel_create_new_notice" - top="250" + top_pad="10" width="265"> <text follows="left|top" type="string" - length="1" font="SansSerifBig" height="16" layout="topleft" @@ -147,19 +143,17 @@ the General tab. follows="left|top" type="string" word_wrap="true" - length="1" height="90" layout="topleft" left_delta="0" name="lbl2" top_pad="4" - width="196"> - You must enter a subject to send a notice. You can add a single item to a notice by dragging it from your inventory to this panel. Attached items must be copiable and transferrable, and you can't send a folder. + width="195"> + You can add a single item to a notice by dragging it from your inventory to this panel. Attached items must be copiable and transferrable, and you can't send a folder. </text> <text follows="left|top" type="string" - length="1" halign="left" height="16" layout="topleft" @@ -182,7 +176,6 @@ the General tab. <text follows="left|top" type="string" - length="1" halign="left" height="16" layout="topleft" @@ -205,7 +198,6 @@ the General tab. <text follows="left|top" type="string" - length="1" halign="left" height="16" layout="topleft" @@ -288,11 +280,10 @@ the General tab. layout="topleft" left="0" name="panel_view_past_notice" - top="250" + top="197" width="265"> <text type="string" - length="1" font="SansSerifBig" height="16" layout="topleft" @@ -305,7 +296,6 @@ the General tab. </text> <text type="string" - length="1" height="16" word_wrap="true" layout="topleft" @@ -317,7 +307,6 @@ the General tab. </text> <text type="string" - length="1" halign="left" height="16" layout="topleft" @@ -342,7 +331,6 @@ the General tab. width="200" /> <text type="string" - length="1" halign="left" height="16" layout="topleft" diff --git a/indra/newview/skins/default/xui/en/panel_group_notify.xml b/indra/newview/skins/default/xui/en/panel_group_notify.xml index a39a681f83..8ebf1b69a7 100644 --- a/indra/newview/skins/default/xui/en/panel_group_notify.xml +++ b/indra/newview/skins/default/xui/en/panel_group_notify.xml @@ -9,7 +9,7 @@ top="5" left="5" mouse_opaque="true" name="group_icon"/> <text type="string" length="1" follows="left|top|right|bottom" font="SansSerifBigBold" height="20" layout="topleft" left="60" name="title" - text_color="GroupNotifyTextColor" top="20" width="300"> + text_color="GroupNotifyTextColor" top="20" width="275" use_ellipses="true"> Sender Name / Group Name </text> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_group_roles.xml b/indra/newview/skins/default/xui/en/panel_group_roles.xml index 132c06d028..9ae165cbb9 100644 --- a/indra/newview/skins/default/xui/en/panel_group_roles.xml +++ b/indra/newview/skins/default/xui/en/panel_group_roles.xml @@ -1,11 +1,12 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel border="true" - height="380" + height="490" label="Members & Roles" layout="topleft" left="1" name="roles_tab" + top="490" width="280"> <panel.string name="default_needs_apply_text"> @@ -28,7 +29,7 @@ top="8" width="20" /> --> - <panel + <!--<panel follows="left|top" height="80" layout="topleft" @@ -38,19 +39,17 @@ width="270"> <text type="string" - length="1" font="SansSerifBig" height="16" layout="topleft" left="0" name="static" - top="4" + top="0" width="270"> Members & Roles </text> <text type="string" - length="1" word_wrap="true" height="40" layout="topleft" @@ -72,19 +71,17 @@ width="270"> <text type="string" - length="1" font="SansSerifBig" height="16" layout="topleft" left="0" name="static" - top="4" + top="0" width="270"> Roles </text> <text type="string" - length="1" word_wrap="true" height="40" layout="topleft" @@ -97,7 +94,6 @@ </text> <text type="string" - length="1" word_wrap="true" height="40" layout="topleft" @@ -109,7 +105,6 @@ </text> <text type="string" - length="1" word_wrap="true" height="16" layout="topleft" @@ -122,7 +117,6 @@ </text> <text type="string" - length="1" word_wrap="true" height="16" layout="topleft" @@ -139,24 +133,22 @@ layout="topleft" left_delta="0" name="actions_header" - top_delta="20" + top_delta="0" visible="false" width="270"> <text type="string" - length="1" font="SansSerifBig" height="16" layout="topleft" left="0" name="static" - top="-16" + top="0" width="270"> Abilities </text> <text type="string" - length="1" word_wrap="true" height="40" layout="topleft" @@ -166,14 +158,15 @@ width="270"> You can view an Ability's Description and which Roles and Members can execute the Ability. </text> - </panel> + </panel> --> <tab_container + follows="left|top" height="180" layout="topleft" - left_delta="0" + left="5" name="roles_tab_container" tab_position="top" - top="80" + top="10" width="265"> <panel border="true" @@ -183,7 +176,7 @@ left="1" name="members_sub_tab" tool_tip="Members" - top="16" + top="17" class="panel_group_members_subtab" width="265"> <panel.string @@ -192,7 +185,26 @@ Select multiple Members by holding the Ctrl key and clicking on their names. </panel.string> - <line_editor + <search_editor + layout="topleft" + top="10" + left="4" + width="255" + height="20" + follows="left|top|right" + max_length="250" + label="Filter People" + name="filter_input" + font="SansSerif" + background_image="TextField_Search_Off" + text_pad_left="10" + text_color="black"> + <search_button label="" + top_pad="4" + left_pad="6" + /> + </search_editor> + <!--<line_editor border_style="line" border_thickness="1" follows="left|top" @@ -221,17 +233,18 @@ clicking on their names. left_pad="0" name="show_all_button" top_delta="0" - width="80" /> + width="80" /> --> <name_list column_padding="0" draw_heading="true" heading_height="14" height="100" + follows="left|top" layout="topleft" left="4" multi_select="true" name="member_list" - top_pad="4" + top_pad="6" width="255"> <name_list.columns label="Member" @@ -280,7 +293,7 @@ clicking on their names. left_delta="0" name="roles_sub_tab" class="panel_group_roles_subtab" - top_delta="0" + top="17" width="265"> <panel.string name="help_text"> @@ -305,7 +318,26 @@ including the Everyone and Owner Roles. name="power_partial_icon"> checkbox_enabled_false.tga </panel.string> - <line_editor + <search_editor + layout="topleft" + top="10" + left="4" + width="255" + height="20" + follows="left|top|right" + max_length="250" + label="Filter Roles" + name="filter_input" + font="SansSerif" + background_image="TextField_Search_Off" + text_pad_left="10" + text_color="black"> + <search_button label="" + top_pad="4" + left_pad="6" + /> + </search_editor> + <!--<line_editor border_style="line" border_thickness="1" follows="left|top" @@ -334,7 +366,7 @@ including the Everyone and Owner Roles. left_pad="0" name="show_all_button" top_delta="0" - width="80" /> + width="80" /> --> <scroll_list column_padding="0" draw_heading="true" @@ -362,7 +394,7 @@ including the Everyone and Owner Roles. <button height="20" font="SansSerifSmall" - label="Create New Role" + label="Add Role" layout="topleft" left_delta="0" name="role_create" @@ -386,14 +418,34 @@ including the Everyone and Owner Roles. left_delta="0" name="actions_sub_tab" class="panel_group_actions_subtab" - top_delta="0" + top="17" + tool_tip="You can view an Ability's Description and which Roles and Members can execute the Ability." width="265"> <panel.string name="help_text"> Abilities allow Members in Roles to do specific things in this group. There's a broad variety of Abilities. </panel.string> - <line_editor + <search_editor + layout="topleft" + top="10" + left="4" + width="255" + height="20" + follows="left|top|right" + max_length="250" + label="Filter Abilities" + name="filter_input" + font="SansSerif" + background_image="TextField_Search_Off" + text_pad_left="10" + text_color="black"> + <search_button label="" + top_pad="4" + left_pad="6" + /> + </search_editor> + <!--<line_editor border_style="line" border_thickness="1" follows="left|top" @@ -422,7 +474,7 @@ things in this group. There's a broad variety of Abilities. left_pad="0" name="show_all_button" top_delta="0" - width="80" /> + width="80" /> --> <scroll_list column_padding="0" draw_stripes="false" @@ -455,18 +507,19 @@ things in this group. There's a broad variety of Abilities. </panel> </tab_container> <panel - height="50" + height="170" layout="topleft" + follows="left|top" left="10" name="members_footer" top_pad="10" width="265"> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" + follows="left|top" left="0" name="static" top_pad="0" @@ -475,10 +528,10 @@ things in this group. There's a broad variety of Abilities. </text> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" + follows="left|top" left_pad="35" name="static2" top_delta="0" @@ -487,8 +540,8 @@ things in this group. There's a broad variety of Abilities. </text> <scroll_list draw_stripes="false" - enabled="false" - height="75" + follows="left|top" + height="150" layout="topleft" left="0" name="member_assigned_roles" @@ -505,9 +558,9 @@ things in this group. There's a broad variety of Abilities. </scroll_list> <scroll_list draw_stripes="false" - enabled="false" - height="75" + height="150" layout="topleft" + follows="left|top" left_pad="5" name="member_allowed_actions" tool_tip="For Details of each Allowed Ability see the Abilities tab." @@ -533,7 +586,6 @@ things in this group. There's a broad variety of Abilities. width="270"> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -545,7 +597,6 @@ things in this group. There's a broad variety of Abilities. </text> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -557,11 +608,10 @@ things in this group. There's a broad variety of Abilities. </text> <line_editor type="string" - length="1" border_style="line" border_thickness="1" follows="left|top" - height="16" + height="20" layout="topleft" left="0" max_length="20" @@ -572,7 +622,6 @@ things in this group. There's a broad variety of Abilities. </line_editor> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -584,11 +633,10 @@ things in this group. There's a broad variety of Abilities. </text> <line_editor type="string" - length="1" border_style="line" border_thickness="1" follows="left|top" - height="16" + height="20" layout="topleft" left_delta="0" max_length="20" @@ -599,7 +647,6 @@ things in this group. There's a broad variety of Abilities. </line_editor> <text_editor type="string" - length="1" halign="left" height="48" hide_scrollbar="true" @@ -614,7 +661,6 @@ things in this group. There's a broad variety of Abilities. </text_editor> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -626,7 +672,6 @@ things in this group. There's a broad variety of Abilities. </text> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -639,8 +684,7 @@ things in this group. There's a broad variety of Abilities. </text> <name_list draw_stripes="false" - enabled="false" - height="75" + height="150" layout="topleft" left="0" name="role_assigned_members" @@ -657,7 +701,7 @@ things in this group. There's a broad variety of Abilities. width="130" /> <scroll_list draw_stripes="false" - height="75" + height="150" layout="topleft" left="135" name="role_allowed_actions" @@ -689,7 +733,6 @@ things in this group. There's a broad variety of Abilities. width="265"> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -701,7 +744,6 @@ things in this group. There's a broad variety of Abilities. </text> <text_editor type="string" - length="1" enabled="false" halign="left" height="48" @@ -717,7 +759,6 @@ things in this group. There's a broad variety of Abilities. </text_editor> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -729,7 +770,6 @@ things in this group. There's a broad variety of Abilities. </text> <text type="string" - length="1" font="SansSerif" height="16" layout="topleft" @@ -740,20 +780,18 @@ things in this group. There's a broad variety of Abilities. Members with Ability </text> <scroll_list - enabled="false" - height="75" + height="150" layout="topleft" left="0" name="action_roles" top="90" width="130" /> <name_list - enabled="false" - height="75" + height="150" layout="topleft" left_pad="5" name="action_members" top_delta="0" width="130" /> </panel> -</panel>
\ No newline at end of file +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_hud.xml b/indra/newview/skins/default/xui/en/panel_hud.xml new file mode 100644 index 0000000000..3e9614a9b7 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_hud.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + follows="left|right|top|bottom" + height="728" + layout="topleft" + left="0" + mouse_opaque="false" + name="hud" + top="778" + width="1024" /> diff --git a/indra/newview/skins/default/xui/en/panel_instant_message.xml b/indra/newview/skins/default/xui/en/panel_instant_message.xml new file mode 100644 index 0000000000..169fde7b47 --- /dev/null +++ b/indra/newview/skins/default/xui/en/panel_instant_message.xml @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<panel + background_visible="true" + bevel_style="in" + bg_alpha_color="0.3 0.3 0.3 0" + height="140" + label="im_panel" + layout="topleft" + left="0" + name="im_panel" + top="0" + width="350"> + <panel + background_visible="true" + bevel_style="in" + bg_alpha_color="black" + follows="top" + height="30" + label="im_header" + layout="topleft" + left="5" + name="im_header" + top="5" + width="340"> + <avatar_icon + follows="right" + height="20" + image_name="icon_avatar_online.tga" + layout="topleft" + left="5" + mouse_opaque="true" + name="avatar" + top="5" + width="20" /> + <text + follows="left|right" + font="SansSerifBigBold" + height="20" + layout="topleft" + left_pad="10" + name="user_name" + text_color="white" + top="5" + value="Darth Vader" + width="250" /> + <text + follows="right" + font="SansSerifBig" + height="20" + layout="topleft" + left_pad="10" + name="time_box" + text_color="white" + top="5" + value="23:30" + width="50" /> + </panel> + <text + follows="left|bottom|right" + height="60" + layout="topleft" + left="10" + name="message" + text_color="white" + top="40" + use_ellipses="true" + value="MESSAGE" + width="330" + word_wrap="true" /> + <button + follows="bottom" + font="SansSerifBigBold" + height="25" + label="reply" + layout="topleft" + left="120" + name="reply" + top="110" + width="110" /> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_login.xml b/indra/newview/skins/default/xui/en/panel_login.xml index 5ee73c1242..41294259e3 100644 --- a/indra/newview/skins/default/xui/en/panel_login.xml +++ b/indra/newview/skins/default/xui/en/panel_login.xml @@ -26,6 +26,7 @@ layout="topleft" left="0" name="login_html" + hide_loading="true" right="-1" start_url="data:text/html,%3Chtml%3E%3Chead%3E%3C/head%3E%3Cbody bgcolor=%22#000000%22 text=%22ffffff%22%3E%3Ch1%3E%3Ctt%3Eloading...%3C/tt%3E%3C/h1%3E %3C/body%3E %3C/html%3E" top="1" /> diff --git a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml index 8d90c6ebf0..dee911a45d 100644 --- a/indra/newview/skins/default/xui/en/panel_navigation_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_navigation_bar.xml @@ -116,12 +116,12 @@ follows="right|top" halign="right" height="22" - label="Search All" + label="Search" layout="topleft" left_pad="7" mouse_opaque="false" name="search_input" - tool_tip="Search All" + tool_tip="Search" top_delta="0" width="200" /> </panel> @@ -132,6 +132,7 @@ layout="topleft" left="5" name="favorite" + chevron_button_tool_tip="Show more of My Favorites" top="32" width="590" /> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_notes.xml b/indra/newview/skins/default/xui/en/panel_notes.xml index e616389c36..12badfccf0 100644 --- a/indra/newview/skins/default/xui/en/panel_notes.xml +++ b/indra/newview/skins/default/xui/en/panel_notes.xml @@ -2,120 +2,143 @@ <panel bevel_style="in" follows="left|top|right|bottom" - height="570" - width="295" - border="false" + height="420" + label="Notes & Privacy" layout="topleft" left="0" + name="panel_notes" top="0" - label="Notes & Privacy" - name="panel_notes"> - <scroll_container - color="DkGray2" - follows="left|top|right|bottom" - height="570" - width="265" - layout="topleft" - left="5" - top_pad="0" - bevel_style="in" - opaque="true" - name="profile_scroll" - reserve_scroll_corner="false"> - <text - type="string" - length="1" - follows="left|top" - font="SansSerifBold" - height="16" - width="225" - left="0" - name="status_message" - text_color="white" - top=""> - My private notes: - </text> - <text_editor - height="200" - follows="left|top" - width="243" - hide_scrollbar="true" - left="10" - max_length="1000" - name="notes_edit" - text_color="black" - top_pad="10" - word_wrap="true" /> - <text - type="string" - length="1" - follows="left|top" - font="SansSerifBold" - height="16" - width="225" - left="10" - name="status_message2" - text_color="white" - top_pad="30"> - Let this person: - </text> - <check_box - enabled="false" - follows="left|top" - width="230" - height="20" - label="See my online status" - left="20" - top_pad="10" - name="status_check" /> - <check_box - enabled="false" - follows="left|top" - width="230" - height="20" - label="See me on the map" - left="20" - top_pad="10" - name="map_check"/> - <check_box - enabled="false" - follows="left|top" - width="230" - height="20" - label="Edit, delete or take my objects" - left="20" - top_pad="10" - name="objects_check" /> - </scroll_container> - <panel - follows="bottom|left" - height="30" - width="280" + width="285"> + <layout_stack + name="layout" + orientation="vertical" + follows="all" layout="topleft" - left="10" - top_pad="5" - name="notes_buttons_panel"> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Teleport" + left="0" + top="0" + height="420" + width="285" + border_size="0"> + <panel + name="notes_stack" + follows="all" layout="topleft" - left="0" - name="teleport_btn" - enabled="false" top="0" - width="75" /> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Show on Map" + left="0" + height="390" + width="285"> + <scroll_container + color="DkGray2" + follows="left|top|right|bottom" + height="390" + layout="topleft" + left="0" + name="profile_scroll" + opaque="true" + top="0" + width="285"> + <panel + height="350" + layout="topleft" + name="profile_scroll_panel" + top="0" + left="0" + width="240"> + <text + follows="left|top" + font="SansSerifBold" + height="16" + layout="topleft" + left="10" + name="status_message" + text_color="white" + top="0" + value="My private notes:" + width="230" /> + <text_editor + follows="left|top" + height="200" + hide_scrollbar="true" + layout="topleft" + left="10" + max_length="1000" + name="notes_edit" + text_color="black" + top_pad="10" + width="235" + word_wrap="true" /> + <text + follows="left|top" + font="SansSerifBold" + height="16" + layout="topleft" + left="10" + name="status_message2" + text_color="white" + top_pad="10" + value="Let this person:" + width="225" /> + <check_box + enabled="false" + height="20" + label="See my online status" + layout="topleft" + left="20" + name="status_check" + top_pad="0" + width="230" /> + <check_box + enabled="false" + height="20" + label="See me on the map" + layout="topleft" + left="20" + name="map_check" + top_pad="0" + width="230" /> + <check_box + enabled="false" + height="20" + label="Edit, delete or take my objects" + layout="topleft" + left="20" + name="objects_check" + top_pad="0" + width="230" /> + </panel> + </scroll_container> + </panel> + <panel + follows="bottom" + height="30" layout="topleft" - left_pad="0" - name="show_on_map_btn" - enabled="false" - top="0" - width="105" /> - </panel> + left="10" + name="notes_buttons_panel" + top_pad="5" + width="280" + auto_resize="false"> + <button + enabled="false" + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + label="Teleport" + layout="topleft" + left="0" + name="teleport_btn" + top="0" + width="75" /> + <button + enabled="false" + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + label="Map" + layout="topleft" + name="show_on_map_btn" + top="0" + left_pad="5" + width="85" /> + </panel> + </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_people.xml b/indra/newview/skins/default/xui/en/panel_people.xml index bccc04e30c..fce31a2c68 100644 --- a/indra/newview/skins/default/xui/en/panel_people.xml +++ b/indra/newview/skins/default/xui/en/panel_people.xml @@ -1,502 +1,524 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - layout="topleft" - font="SansSerifBigBold" - top="0" - left="0" - width="305" - height="465" - follows="left|top|right|bottom" - background_visible="true" - bg_alpha_color="DkGray2" - bevel_style="in" - label="People" - name="people_panel"> - <filter_editor - layout="topleft" - top="3" - left="10" - width="256" - height="23" - follows="left|top|right" - max_length="270" - name="filter_input" - font="SansSerif" - background_image="TextField_Search_Off" - text_pad_left="23" - text_color="black" /> - <button - name="people_search" - layout="topleft" - top="5" - left="23" - width="13" - height="13" - scale_image="false" - follows="left|top|right" - font="SansSerifBigBold" - image_selected="Search" - image_unselected="Search" - picture_style="true"/> - <tab_container - layout="topleft" - top="30" - left="10" - width="295" - height="400" - follows="left|top|right|bottom" - name="tabs" - tab_position="top"> - <panel - layout="topleft" - height="390" - width="280" - follows="left|top|right|bottom" - background_visible="false" - bevel_style="none" - label="Nearby" - name="nearby_panel"> - <avatar_list - layout="topleft" - top="2" - left="0" - width="280" - height="357" - follows="left|top|right|bottom" - name="avatar_list" - volume_column_width="20"/> - <panel - layout="topleft" - bottom="390" - left="0" - width="280" - height="30" - follows="left|right|bottom" - background_visible="true" - bevel_style="none" - label="bottom_panel" - name="bottom_panel"> - <button - name="gear_btn" - enabled="false" - layout="topleft" - top="7" - left="20" - width="18" - height="18" - follows="bottom|right" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="OptionsMenu_Press" - image_unselected="OptionsMenu_Off" - picture_style="true"/> + background_visible="true" + follows="all" + height="400" + label="People" + layout="topleft" + min_height="350" + min_width="240" + name="people_panel" + width="305"> + <string + name="no_people" + value="No people" /> + <string + name="no_one_near" + value="No-one near" /> + <string + name="no_friends_online" + value="No friends online" /> + <string + name="no_friends_offline" + value="No friends offline" /> + <string + name="no_groups" + value="No groups" /> + <string + name="people_filter_label" + value="Filter People" /> + <string + name="groups_filter_label" + value="Filter Groups" /> + <filter_editor + background_image="TextField_Search_Off" + follows="left|top|right" + font="SansSerif" + height="23" + layout="topleft" + left="15" + max_length="270" + name="filter_input" + text_color="black" + text_pad_left="26" + top="3" + width="256" /> + <button + follows="left|top|right" + height="13" + image_selected="Search" + image_unselected="Search" + layout="topleft" + left="20" + name="people_search" + picture_style="true" + scale_image="false" + top="8" + width="13" /> + <tab_container + follows="left|top|right|bottom" + height="326" + layout="topleft" + left="9" + name="tabs" + tab_position="top" + top_pad="15" + width="285"> + <panel + bevel_style="none" + follows="left|top|right|bottom" + height="390" + label="Nearby" + layout="topleft" + name="nearby_panel" + width="285"> + <avatar_list + follows="left|top|right|bottom" + height="357" + layout="topleft" + left="0" + name="avatar_list" + top="2" + volume_column_width="20" + width="285" /> + <panel + background_visible="true" + bevel_style="none" + bottom="390" + follows="left|right|bottom" + height="30" + label="bottom_panel" + layout="topleft" + left="0" + name="bottom_panel" + width="285"> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="OptionsMenu_Press" + image_unselected="OptionsMenu_Off" + layout="topleft" + left="20" + name="nearby_view_sort_btn" + picture_style="true" + tool_tip="Change sort and view of Residents list" + top="7" + width="18" /> + </panel> + </panel> + <panel + background_visible="true" + bevel_style="none" + follows="left|top|right|bottom" + height="390" + label="Friends" + layout="topleft" + name="friends_panel" + width="285"> + <accordion + follows="left|top|right|bottom" + height="357" + layout="topleft" + left="0" + name="friends_accordion" + top="2" + width="285"> + <accordion_tab + can_resize="false" + layout="topleft" + min_height="100" + name="tab_online" + title="Online"> + <panel + follows="all" + height="150" + layout="topleft" + left="0" + name="tab_online_panel" + top="100" + width="285"> + <avatar_list + draw_heading="false" + follows="all" + height="145" + layout="topleft" + left="0" + name="avatars_online" + top="0" + width="285" /> + </panel> + </accordion_tab> + <accordion_tab + can_resize="false" + layout="topleft" + min_height="100" + name="tab_offline" + title="Offline"> + <panel + follows="all" + height="260" + layout="topleft" + left="0" + name="tab_offline_panel" + top="100" + width="285"> + <avatar_list + draw_heading="false" + follows="all" + height="255" + layout="topleft" + left="0" + name="avatars_offline" + top="0" + width="285" /> + </panel> + </accordion_tab> + </accordion> + <panel + background_visible="true" + bevel_style="none" + bottom="390" + follows="left|right|bottom" + height="30" + label="bottom_panel" + layout="topleft" + left="0" + name="bottom_panel" + width="285"> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="OptionsMenu_Press" + image_unselected="OptionsMenu_Off" + layout="topleft" + left="20" + name="friends_viewsort_btn" + picture_style="true" + tool_tip="Change sort and view of Friends list" + top="7" + width="18" /> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="AddItem_Press" + image_unselected="AddItem_Off" + layout="topleft" + left_pad="5" + name="add_btn" + picture_style="true" + tool_tip="Offer friendship to a resident" + top_delta="0" + width="18" /> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="TrashItem_Press" + image_unselected="TrashItem_Off" + layout="topleft" + left_pad="180" + name="del_btn" + picture_style="true" + tool_tip="Remove selected person from your Friends list" + top_delta="0" + width="18" /> + </panel> </panel> - </panel> - <panel - layout="topleft" - height="390" - width="280" - follows="left|top|right|bottom" - background_visible="true" - bevel_style="none" - label="Friends" - name="friends_panel"> - <avatar_list - layout="topleft" - top="2" - left="0" - width="280" - height="357" - follows="left|top|right|bottom" - name="avatar_list"/> - <panel - layout="topleft" - bottom="390" - left="0" - width="280" - height="30" - follows="left|right|bottom" - background_visible="true" - bevel_style="none" - label="bottom_panel" - name="bottom_panel"> - <button - name="gear_btn" - enabled="false" - layout="topleft" - top="7" - left="20" - width="18" - height="18" - follows="bottom|right" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="OptionsMenu_Press" - image_unselected="OptionsMenu_Off" - picture_style="true"/> - <button - name="add_btn" - layout="topleft" - top_delta="0" - left_pad="5" - width="18" - height="18" - follows="bottom|left" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="AddItem_Press" - image_unselected="AddItem_Off" - picture_style="true"/> - <button - name="del_btn" - layout="topleft" - top_delta="0" - left_pad="180" - width="18" - height="18" - follows="bottom|left" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="TrashItem_Press" - image_unselected="TrashItem_Off" - picture_style="true"/> + <panel + background_visible="true" + bevel_style="none" + follows="left|top|right|bottom" + height="390" + label="Groups" + layout="topleft" + name="groups_panel" + width="285"> + <group_list + follows="left|top|right|bottom" + height="357" + layout="topleft" + left="0" + name="group_list" + top="2" + width="285" /> + <panel + background_visible="true" + bevel_style="none" + bottom="390" + follows="left|right|bottom" + height="30" + label="bottom_panel" + layout="topleft" + left="0" + name="bottom_panel" + width="285"> + <button + enabled="false" + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="OptionsMenu_Press" + image_unselected="OptionsMenu_Off" + layout="topleft" + left="20" + name="gear_btn" + picture_style="true" + tool_tip="Change sort and view of Groups list" + top="7" + width="18" /> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="AddItem_Press" + image_unselected="AddItem_Off" + layout="topleft" + left_pad="5" + name="plus_btn" + picture_style="true" + tool_tip="Join group/Create new group" + top_delta="0" + width="18" /> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="10" + image_hover_selected="group_activate_btn.tga" + image_selected="group_activate_btn.tga" + image_unselected="group_activate_btn.tga" + layout="topleft" + left_pad="24" + name="activate_btn" + picture_style="true" + tool_tip="Activate selected group" + top_delta="5" + width="10" /> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="TrashItem_Press" + image_unselected="TrashItem_Off" + layout="topleft" + left_pad="146" + name="minus_btn" + picture_style="true" + tool_tip="Leave selected group" + top_delta="-5" + width="18" /> + </panel> </panel> - </panel> - <panel - layout="topleft" - height="390" - width="280" - follows="left|top|right|bottom" - background_visible="true" - bevel_style="none" - label="Groups" - name="groups_panel"> - <group_list - layout="topleft" - top="2" - left="0" - width="280" - height="357" - follows="left|top|right|bottom" - name="group_list"/> - <panel - layout="topleft" - bottom="390" - left="0" - width="280" - height="30" - follows="left|right|bottom" - background_visible="true" - bevel_style="none" - label="bottom_panel" - name="bottom_panel"> - <button - name="gear_btn" - enabled="false" - layout="topleft" - top="7" - left="20" - width="18" - height="18" - follows="bottom|right" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="OptionsMenu_Press" - image_unselected="OptionsMenu_Off" - picture_style="true"/> - <button - name="plus_btn" - layout="topleft" - top_delta="0" - left_pad="5" - width="18" - height="18" - follows="bottom|left" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="AddItem_Press" - image_unselected="AddItem_Off" - picture_style="true"/> - <button - name="activate_btn" - layout="topleft" - top_delta="5" - left_pad="24" - width="10" - height="10" - follows="bottom|left" - font="SansSerifBigBold" - image_hover_selected="group_activate_btn.tga" - image_selected="group_activate_btn.tga" - image_unselected="group_activate_btn.tga" - picture_style="true"/> - <button - name="minus_btn" - layout="topleft" - top_delta="-5" - left_pad="146" - width="18" - height="18" - follows="bottom|left" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="TrashItem_Press" - image_unselected="TrashItem_Off" - picture_style="true"/> + <panel + background_visible="true" + bevel_style="none" + follows="left|top|right|bottom" + height="390" + label="Recent" + layout="topleft" + name="recent_panel" + width="285"> + <avatar_list + follows="left|top|right|bottom" + height="357" + layout="topleft" + left="0" + name="avatar_list" + top="2" + width="285" /> + <panel + background_visible="true" + bevel_style="none" + bottom="390" + follows="left|right|bottom" + height="30" + label="bottom_panel" + layout="topleft" + left="0" + name="bottom_panel" + width="285"> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="OptionsMenu_Press" + image_unselected="OptionsMenu_Off" + layout="topleft" + left="20" + name="recent_viewsort_btn" + picture_style="true" + tool_tip="Change sort and view of Residents list" + top="7" + width="18" /> + </panel> </panel> - </panel> - <panel - layout="topleft" - height="390" - width="280" - follows="left|top|right|bottom" - background_visible="true" - bevel_style="none" - label="History" - name="recent_panel"> - <avatar_list - layout="topleft" - top="2" - left="0" - width="280" - height="357" - follows="left|top|right|bottom" - name="avatar_list"/> - <panel - layout="topleft" - bottom="390" - left="0" - width="280" - height="30" - follows="left|right|bottom" - background_visible="true" - bevel_style="none" - label="bottom_panel" - name="bottom_panel"> - <button - name="gear_btn" - enabled="false" - layout="topleft" - top="7" - left="20" - width="18" - height="18" - follows="bottom|right" - font="SansSerifBigBold" - hover_glow_amount="0.15" - image_selected="OptionsMenu_Press" - image_unselected="OptionsMenu_Off" - picture_style="true"/> - </panel> - </panel> - </tab_container> - <layout_stack - name="button_bar" - follows="left|right|bottom" - bottom="6" - left="6" - width="295" - height="27" - orientation="horizontal" - animate="false" - border_size="0"> - <layout_panel - name="view_profile_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="85" - width="85" - visible="true"> - <button - label="View Profile" - name="view_profile_btn" - layout="topleft" - width="85" - height="25" - follows="top|left|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel - name="add_friend_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="85" - width="85" - visible="true"> - <button - label="Add Friend" - name="add_friend_btn" - layout="topleft" - width="85" height="25" - follows="top|left|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel - name="group_info_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="80" - width="80" - visible="true"> - <button - label="Group Info" - name="group_info_btn" - layout="topleft" - width="80" - height="25" - follows="top|left|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel name="chat_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="45" - width="45" - visible="true"> - <button - label="Chat" - name="chat_btn" - layout="topleft" - width="45" height="25" - follows="top|left|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel - name="im_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="35" - width="35" - visible="true"> - <button - label="IM" - name="im_btn" - layout="topleft" - width="35" - height="25" - follows="top|left|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel - name="call_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="40" - width="40" - visible="false"> - <button - label="Call" - name="call_btn" - enabled="false" - layout="topleft" - width="40" - height="25" - follows="top|left|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel - name="teleport_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="65" - width="65"> - <button - label="Teleport" - name="teleport_btn" - width="65" - height="25" - layout="topleft" - follows="left|top|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel - name="share_btn_panel" - enabled="false" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="50" - width="50" - visible="false"> - <button - label="Share" - name="share_btn" - enabled="false" - layout="topleft" - width="50" - height="25" - follows="top|left|right" - font="SansSerifSmallBold"/> - </layout_panel> - <layout_panel - name="more_btn_panel" - border="false" - bottom="0" - default_tab_group="1" - follows="left|top|right" - height="25" - left="0" - min_width="40" - width="40" - visible="true"> - <button - label=">>" - name="more_btn" - enabled="false" - layout="topleft" - width="40" - height="25" - follows="top|right" - font="SansSerifSmallBold"/> - </layout_panel> - </layout_stack> - <string name="no_people"> - No people - </string> - <string name="no_one_near"> - No-one near - </string> - <string name="no_friends"> - No friends - </string> - <string name="no_groups"> - No groups - </string> - <string name="people_filter_label"> - Filter People - </string> - <string name="groups_filter_label"> - Filter Groups - </string> + </tab_container> + <layout_stack + animate="false" + border_size="0" + follows="left|right|bottom" + height="27" + layout="topleft" + left="10" + name="button_bar" + orientation="horizontal" + width="295"> + <layout_panel + default_tab_group="1" + follows="left|top|right" + height="25" + layout="topleft" + left="0" + name="view_profile_btn_panel" + top="-25" + width="65"> + <button + follows="top|left|right" + font="SansSerifSmallBold" + height="25" + label="Profile" + layout="topleft" + name="view_profile_btn" + tool_tip="Show picture, groups, and other residents information" + width="65" /> + </layout_panel> + <layout_panel + default_tab_group="1" + follows="left|top|right" + height="25" + layout="topleft" + left_delta="0" + min_width="85" + name="add_friend_btn_panel" + top_delta="0" + width="85"> + <button + follows="top|left|right" + font="SansSerifSmallBold" + height="25" + label="Add Friend" + layout="topleft" + name="add_friend_btn" + tool_tip="Add selected resident to your Friends List" + width="85" /> + </layout_panel> + <layout_panel + default_tab_group="1" + follows="left|top|right" + height="25" + layout="topleft" + min_width="80" + name="group_info_btn_panel" + width="80"> + <button + follows="top|left|right" + font="SansSerifSmallBold" + height="25" + label="Group Profile" + layout="topleft" + name="group_info_btn" + tool_tip="Show group information" + width="80" /> + </layout_panel> + <layout_panel + default_tab_group="1" + follows="left|top|right" + height="25" + layout="topleft" + min_width="45" + name="chat_btn_panel" + top_delta="0" + width="45"> + <button + follows="top|left|right" + font="SansSerifSmallBold" + height="25" + label="Chat" + layout="topleft" + name="chat_btn" + tool_tip="Open chat session" + width="45" /> + </layout_panel> + <layout_panel + default_tab_group="1" + follows="left|top|right" + height="25" + layout="topleft" + min_width="35" + name="im_btn_panel" + top_delta="0" + width="35"> + <button + follows="top|left|right" + font="SansSerifSmallBold" + height="25" + label="IM" + layout="topleft" + name="im_btn" + tool_tip="Open instant message session" + width="35" /> + </layout_panel> + <layout_panel + default_tab_group="1" + follows="left|top|right" + height="25" + layout="topleft" + min_width="40" + name="call_btn_panel" + top_delta="0" + visible="false" + width="40"> + <button + enabled="false" + follows="top|left|right" + font="SansSerifSmallBold" + height="25" + label="Call" + layout="topleft" + name="call_btn" + width="40" /> + </layout_panel> + <layout_panel + default_tab_group="1" + follows="left|top|right" + height="25" + layout="topleft" + min_width="65" + name="teleport_btn_panel" + top_delta="0" + width="65"> + <button + follows="left|top|right" + font="SansSerifSmallBold" + height="25" + label="Teleport" + layout="topleft" + name="teleport_btn" + tool_tip="Offer teleport" + width="65" /> + </layout_panel> + <layout_panel + default_tab_group="1" + enabled="false" + follows="left|top|right" + height="25" + layout="topleft" + min_width="50" + name="share_btn_panel" + top_delta="0" + visible="false" + width="50"> + <button + enabled="false" + follows="top|left|right" + font="SansSerifSmallBold" + height="25" + label="Share" + layout="topleft" + name="share_btn" + width="50" /> + </layout_panel> + </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_pick_info.xml b/indra/newview/skins/default/xui/en/panel_pick_info.xml index 1425246540..c8bde77a94 100644 --- a/indra/newview/skins/default/xui/en/panel_pick_info.xml +++ b/indra/newview/skins/default/xui/en/panel_pick_info.xml @@ -1,69 +1,75 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - follows="left|top|right|bottom" bevel_style="in" - height="625" - width="280" + follows="left|top|right|bottom" + height="480" layout="topleft" left="0" name="panel_pick_info" - top="0"> + top="0" + width="265"> <text - type="string" - length="1" follows="top" font="SansSerifHugeBold" height="15" layout="topleft" - left="40" + left="10" name="title" text_color="white" - top="0" - width="150"> - Pick Info - </text> - <button + top="16" + value="Pick Info" + width="150" /> + <button follows="top|right" - right="-25" - top="10" - width="20" - height="20" + height="25" image_overlay="BackArrow_Off" layout="topleft" name="back_btn" - picture_style="true" /> - <panel - follows="left|right|top|bottom" - min_height="300" - width="280" + picture_style="true" + right="-11" + top="16" + width="25" + tab_stop="false" /> + + <scroll_container + color="DkGray2" + follows="left|top|right|bottom" + height="400" layout="topleft" + left="0" + name="pick_info_scroll" + opaque="true" + reserve_scroll_corner="true" + width="265"> + <panel background_visible="true" bg_alpha_color="DkGray2" - left="10" - top="30"> + follows="left|right|top" + height="550" + layout="topleft" + left="0" + min_height="550" + top_pad="0" + width="263"> <texture_picker enabled="false" follows="left|top|right" - height="150" + height="300" layout="topleft" left="10" name="pick_snapshot" - right="-10" - top="10" /> + top="10" + width="245" /> <text - type="string" - length="1" follows="left|top" + font="SansSerifBigBold" height="15" layout="topleft" left="10" name="Name:" - text_color="white"> - Name: - </text> + text_color="white" + value="Name:" /> <text - type="string" - length="1" follows="left|top|right" height="15" layout="topleft" @@ -71,25 +77,20 @@ name="pick_name" right="-10" text_color="white" - word_wrap="true"> - [name] - </text> + value="[name]" + word_wrap="true" /> <text - type="string" follows="left|top" + font="SansSerifBigBold" height="15" layout="topleft" left="10" name="description_label" text_color="white" top_pad="20" - v_pad="0" - valign="center"> - Description: - </text> + valign="center" + value="Description:" /> <text - type="string" - length="1" follows="left|top|right" height="60" layout="topleft" @@ -98,42 +99,47 @@ right="-10" text_color="white" valign="center" - word_wrap="true"> - [description] - </text> + value="[description]" + width="260" + word_wrap="true" /> <text - type="string" - length="1" follows="left|top" + font="SansSerifBigBold" height="20" layout="topleft" left="10" name="location_label" text_color="white" top_pad="20" - valign="bottom"> - Location: - </text> + valign="bottom" + value="Location:" /> <text - type="string" follows="left|top" height="30" layout="topleft" left="10" name="pick_location" text_color="white" - valign="center"> - [loading...] - </text> + valign="center" + value="[loading...]" /> + <icon + follows="top|left" + height="15" + image_name="" + layout="topleft" + mouse_opaque="true" + name="maturity" + width="15" /> </panel> + </scroll_container> <panel follows="left|right|bottom" height="30" layout="topleft" + top_pad="2" left="8" name="buttons" - right="-10" - bottom="660"> + right="-10"> <button follows="bottom|left" font="SansSerifSmallBold" @@ -150,7 +156,7 @@ height="25" label="Teleport" layout="topleft" - left_pad="0" + left_pad="2" name="teleport_btn" top="0" width="80" /> @@ -158,22 +164,11 @@ follows="bottom|left" font="SansSerifSmallBold" height="25" - label="Show on Map" + label="Map" layout="topleft" - left_pad="0" + left_pad="2" name="show_on_map_btn" top="0" width="100" /> - <button - enabled="false" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Verb" - layout="topleft" - left_pad="0" - name="verb_btn" - top="0" - width="50" /> </panel> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_pick_list_item.xml b/indra/newview/skins/default/xui/en/panel_pick_list_item.xml index 78bec6035f..6f4110067b 100644 --- a/indra/newview/skins/default/xui/en/panel_pick_list_item.xml +++ b/indra/newview/skins/default/xui/en/panel_pick_list_item.xml @@ -1,57 +1,57 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - border="false" - follows="top|left|right|bottom" - top="0" + bevel_style="none" + follows="top|left|right" + height="120" + layout="topleft" left="0" - height="100" - width="265" - name="picture_item"> + name="picture_item" + top="0" + width="275"> <texture_picker allow_no_texture="true" default_image_name="None" enabled="false" follows="left|top" - height="100" - width="100" + height="120" layout="topleft" - left="10" - bottom_pad="10" + left="5" mouse_opaque="false" name="picture" tab_stop="false" - top="10" /> + top="5" + width="120" /> <text follows="top|left|right" - font="SansSerifTiny" height="16" layout="topleft" - left="120" + left="135" name="picture_name" - top="10" text_color="white" + top="5" use_ellipses="true" - width="120" /> + width="120" + word_wrap="true" /> <text follows="top|left|right" - font="SansSerifTiny" - height="60" - width="140" - top_pad="0" + height="75" layout="topleft" + left="135" name="picture_descr" + top_pad="10" use_ellipses="true" + width="130" word_wrap="true" /> <button follows="top|right" height="16" - image_selected="Info" - image_unselected="Info" + image_selected="Info_Press" + image_unselected="Info_Off" layout="topleft" name="info_chevron" picture_style="true" + right="262" tab_stop="false" - top="6" - left="233" + top="3" width="16" /> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_picks.xml b/indra/newview/skins/default/xui/en/panel_picks.xml index 2be9f900da..ff161cc2ab 100644 --- a/indra/newview/skins/default/xui/en/panel_picks.xml +++ b/indra/newview/skins/default/xui/en/panel_picks.xml @@ -1,140 +1,112 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - bevel_style="in" follows="left|top|right|bottom" - height="570" - width="295" - border="false" + height="420" + label="Picks" layout="topleft" left="0" + name="panel_picks" top="0" - label="Picks" - name="panel_picks"> - <scroll_container - color="DkGray2" - follows="left|top|right|bottom" - height="570" - width="265" - layout="topleft" - left="5" - top_pad="0" - bevel_style="in" - opaque="true" - name="profile_scroll" - reserve_scroll_corner="false"> -<!-- below is a special title shown for the Agent on the "Picks" tab - <text - type="string" - length="1" - follows="top" - font="SansSerifBold" - height="35" - width="250" - layout="topleft" - left="10" - name="pick_title_agent" - text_color="white" - top_pad="25" - visible="false" - word_wrap="true"> - Tell everyone about your favorite Second Life places... - </text>--> - <panel - height="115" - width="265" - layout="topleft" - left="0" - name="back_panel" - top="0" /> - </scroll_container> - <panel - background_visible="true" - bevel_style="none" - enabled="false" - follows="left|right|bottom" - height="30" - label="bottom_panel" + width="285"> + <layout_stack + name="layout" + orientation="vertical" + follows="all" layout="topleft" left="0" - name="edit_panel" - visible="false" - top_pad="0" - width="280"> - <button - enabled="false" - follows="bottom|left" - font="SansSerifBigBold" - height="18" - hover_glow_amount="0.15" - image_selected="OptionsMenu_Press" - image_unselected="OptionsMenu_Off" - layout="topleft" - left="10" - name="gear_menu_btn" - picture_style="true" - top="5" - width="18" /> - <button - follows="bottom|left" - font="SansSerifBigBold" - height="18" - image_disabled="AddItem_Off" - image_disabled_selected="AddItem_Press" - hover_glow_amount="0.15" - image_selected="AddItem_Press" - image_unselected="AddItem_Off" - layout="topleft" - left="35" - name="new_btn" - picture_style="true" - tool_tip="Create New Pick at Current Location" - top="5" - width="18" /> - <button - follows="bottom|right" - font="SansSerifBigBold" - height="18" - image_disabled="TrashItem_Off" - image_disabled_selected="TrashItem_Press" - hover_glow_amount="0.15" - image_selected="TrashItem_Press" - image_unselected="TrashItem_Off" + top="0" + height="420" + width="284" + border_size="0"> + <scroll_container + color="DkGray2" + follows="left|top|right|bottom" + height="350" layout="topleft" - name="trash_btn" - picture_style="true" - right="-10" - top="5" - width="18" /> - </panel> - <panel - follows="bottom|left" - height="30" - width="280" - layout="topleft" - left="10" - top_pad="5" - name="buttons_cucks"> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Teleport" - layout="topleft" - left="0" - name="teleport_btn" - enabled="false" + left="2" + name="profile_scroll" + opaque="true" top="0" - width="75" /> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Show on Map" - layout="topleft" - left_pad="0" - name="show_on_map_btn" + width="284"> + <panel + height="115" + layout="topleft" + left="0" + name="back_panel" + top="0" + width="284" /> + </scroll_container> + <panel + background_visible="true" + bevel_style="none" enabled="false" - top="0" - width="105" /> - </panel> + auto_resize="false" + follows="bottom" + height="30" + label="bottom_panel" + layout="topleft" + name="edit_panel" + top_pad="5" + width="284"> + <button + enabled="false" + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_selected="OptionsMenu_Press" + image_unselected="OptionsMenu_Off" + layout="topleft" + left="10" + name="gear_menu_btn" + picture_style="true" + top="5" + width="18" /> + <button + follows="bottom|left" + font="SansSerifBigBold" + height="18" + image_disabled="AddItem_Off" + image_disabled_selected="AddItem_Press" + image_selected="AddItem_Press" + image_unselected="AddItem_Off" + layout="topleft" + left="35" + name="new_btn" + picture_style="true" + tool_tip="Create New Pick at Current Location" + top="5" + width="18" /> + <button + follows="bottom|right" + font="SansSerifBigBold" + height="18" + image_disabled="TrashItem_Off" + image_disabled_selected="TrashItem_Press" + image_selected="TrashItem_Press" + image_unselected="TrashItem_Off" + layout="topleft" + name="trash_btn" + picture_style="true" + right="-10" + top="5" + width="18" /> + </panel> + <panel + follows="bottom" + height="30" + auto_resize="false" + layout="topleft" + name="buttons_cucks" + width="284"> + <button + enabled="false" + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + label="Map" + layout="topleft" + name="show_on_map_btn" + width="85" /> + </panel> + </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_places.xml b/indra/newview/skins/default/xui/en/panel_places.xml index 1a88cc55ec..bff28718a7 100644 --- a/indra/newview/skins/default/xui/en/panel_places.xml +++ b/indra/newview/skins/default/xui/en/panel_places.xml @@ -19,112 +19,119 @@ background_image="TextField_Search_Off" follows="left|top|right" font="SansSerif" - height="20" + height="23" label="Filter" layout="topleft" left="15" name="Filter" text_color="black" - text_pad_left="23" + text_pad_left="26" top="3" - width="270" /> + width="256" /> <button follows="left|top|right" - font="SansSerifBigBold" height="13" image_selected="Search" image_unselected="Search" layout="topleft" - left="23" + left="20" name="landmark_search" picture_style="true" scale_image="false" - top="5" + top="8" width="13" /> <tab_container follows="all" height="326" layout="topleft" - left="10" + left="9" name="Places Tabs" tab_position="top" - top_pad="19" - width="280" /> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - image_disabled="widgets/SegmentedBtn_Left_Disabled.png" - image_selected="widgets/SegmentedBtn_Left_Selected.png" - image_unselected="widgets/SegmentedBtn_Left_Off.png" - label="Create" - layout="topleft" - left="10" - name="create_landmark_btn" - top_pad="5" - visible="false" - width="60" /> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - image_disabled="widgets/ComboButton_Disabled.png" - image_selected="widgets/ComboButton_Selected.png" - image_unselected="widgets/ComboButton_Off.png" - label="▼" - layout="topleft" - left_pad="0" - name="folder_menu_btn" - visible="false" - width="20" /> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Teleport" - layout="topleft" - left="10" - name="teleport_btn" - top_delta="0" - width="80" /> - <button - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Show on Map" - layout="topleft" - left_pad="0" - name="map_btn" - top_delta="0" - width="110" /> - <button - enabled="false" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Share" - layout="topleft" - left_pad="0" - name="share_btn" - top_delta="0" - width="60" /> - <button - follows="bottom|right" - font="SansSerifSmallBold" - height="25" - label="▼" - layout="topleft" - left_pad="0" - name="overflow_btn" - top_delta="0" - width="30" /> + top="30" + width="285" /> <panel class="panel_place_info" filename="panel_place_info.xml" follows="all" + height="326" layout="topleft" left="0" name="panel_place_info" - top="-200" + top="30" visible="false" /> + <panel + height="25" + layout="topleft" + left="0" + name="button_panel" + top_pad="10" + width="305"> + <button + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + label="Teleport" + layout="topleft" + left="5" + name="teleport_btn" + top="0" + width="80" /> + <button + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + label="Map" + layout="topleft" + left_pad="5" + name="map_btn" + top="0" + width="80" /> + <button + enabled="false" + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + label="Share" + layout="topleft" + left_pad="5" + name="share_btn" + top="0" + width="60" /> + <button + follows="bottom|right" + font="SansSerifSmallBold" + height="25" + label="▼" + layout="topleft" + left_pad="5" + name="overflow_btn" + top="0" + width="30" /> + <button + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + image_disabled="widgets/SegmentedBtn_Left_Disabled.png" + image_selected="widgets/SegmentedBtn_Left_Selected.png" + image_unselected="widgets/SegmentedBtn_Left_Off.png" + label="Create" + layout="topleft" + left="5" + name="create_landmark_btn" + top="0" + width="60" /> + <button + follows="bottom|left" + font="SansSerifSmallBold" + height="25" + image_disabled="widgets/ComboButton_Disabled.png" + image_selected="widgets/ComboButton_Selected.png" + image_unselected="widgets/ComboButton_Off.png" + label="▼" + layout="topleft" + left_pad="0" + name="folder_menu_btn" + top="0" + width="20" /> + </panel> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml index c154c5d21e..060d84b2e4 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_advanced.xml @@ -10,46 +10,38 @@ name="advanced" top="1" width="517"> - - <!-- ADVANCED --> - - - <check_box - control_name="ArrowKeysMoveAvatar" + <panel.string + name="resolution_format"> + [RES_X] x [RES_Y] + </panel.string> + <panel.string + name="aspect_ratio_text"> + [NUM]:[DEN] + </panel.string> + <check_box + control_name="UseChatBubbles" height="16" - label="Arrow keys always move avatar when chatting" + label="Bubble Chat" layout="topleft" - left_delta="30" - name="arrow_keys_move_avatar_check" + left="30" top="20" - width="237" /> - <text - type="string" - length="1" - follows="left|top" - height="10" - layout="topleft" - left_delta="0" - mouse_opaque="false" - name="text_box7" - top_pad="10" - width="128"> - Bubble Chat: - </text> + name="bubble_text_chat" + width="150" /> + <slider control_name="ChatBubbleOpacity" - follows="left|top" - height="12" + height="16" increment="0.05" initial_value="1" label="Opacity" layout="topleft" - left_delta="0" + left="50" + top_pad="5" + label_width="50" name="bubble_chat_opacity" - top_pad="10" width="200" /> - - <check_box + + <!-- <check_box control_name="UIAutoScale" height="16" label="Resolution independent scale" @@ -57,19 +49,8 @@ left="30" name="ui_auto_scale" top_pad="10" - width="256" /> - <!--text - type="string" - length="1" - follows="left|top" - height="12" - layout="topleft" - left="30" - name="DisplayResLabel" - top_pad="10" - width="128"> - Display Resolution: - </text> + width="256" />--> + <!-- <combo_box height="18" layout="topleft" @@ -80,19 +61,19 @@ <text type="string" length="1" - follows="left|top" - height="12" + height="25" layout="topleft" left="30" + top_pad="20" name="AspectRatioLabel1" tool_tip="width / height" - top_pad="5" - width="160"> - Aspect Ratio: + label_width="50" + width="120"> + Aspect Ratio </text> <combo_box allow_text_entry="true" - height="16" + height="25" layout="topleft" left_pad="0" max_chars="100" @@ -102,89 +83,67 @@ width="150"> <combo_box.item enabled="true" - label="4:3 (Standard CRT)" + label=" 4:3 (Standard CRT)" name="item1" value="1.333333" /> <combo_box.item enabled="true" - label="5:4 (1280x1024 LCD)" + label=" 5:4 (1280x1024 LCD)" name="item2" value="1.25" /> <combo_box.item enabled="true" - label="8:5 (Widescreen)" + label=" 8:5 (Widescreen)" name="item3" value="1.6" /> <combo_box.item enabled="true" - label="16:9 (Widescreen)" + label=" 16:9 (Widescreen)" name="item4" value="1.7777777" /> </combo_box> <check_box control_name="FullScreenAutoDetectAspectRatio" - height="16" - label="Auto-detect ratio" + height="25" + label="Auto-detect" layout="topleft" - left_pad="5" + left_pad="10" name="aspect_auto_detect" - top_delta="1" width="256"> <check_box.commit_callback function="Pref.AutoDetectAspect" /> </check_box> - <text + + <text type="string" length="1" - follows="left|top" height="10" - layout="topleft" left="30" - name=" Camera Options:" - top_pad="10" - width="266"> - Camera Options: - </text> - <text - type="string" - length="1" - follows="left|top" - height="10" - layout="topleft" - left="30" - name="camera_fov_label" - top_pad="10" - width="128"> - Camera View Angle: - </text> - <slider + text_color="EmphasisColor" + name="text_box1" + top_pad="30" + width="270"> +Camera: + </text> + <slider can_edit_text="true" control_name="CameraAngle" decimal_digits="2" + top_pad="10" follows="left|top" height="16" increment="0.025" initial_value="1.57" layout="topleft" - left="128" + label_width="100" + label="View Angle" + left="30" max_val="2.97" min_val="0.17" name="camera_fov" show_text="false" - top_delta="0" - width="128" /> - <text - type="string" - length="1" - follows="left|top" - height="10" - layout="topleft" - left="30" - name="Camera Follow Distance:" - top_pad="10" - width="128"> - Camera Follow Distance: - </text> + width="240" /> + <slider can_edit_text="true" control_name="CameraOffsetScale" @@ -194,67 +153,65 @@ increment="0.025" initial_value="1" layout="topleft" - left_delta="128" + label="Distance" + left="30" + label_width="100" max_val="3" min_val="0.5" name="camera_offset_scale" show_text="false" - top_delta="0" - width="128" /> - + width="240" /> + <text + type="string" + length="1" + height="10" + left="30" + name="text_box1" + width="270"> +Automatic positioning for: + </text> <check_box control_name="EditCameraMovement" - height="16" - label="Automatic Edit Camera Movement" + height="20" + label="Build/Edit" layout="topleft" - left="10" + left="50" name="edit_camera_movement" tool_tip="Use automatic camera positioning when entering and exiting edit mode" - top_pad="5" - width="201" /> + width="280" /> <check_box control_name="AppearanceCameraMovement" height="16" - label="Automatic Appearance Camera Movement" + label="Appearance" layout="topleft" - left_pad="5" name="appearance_camera_movement" tool_tip="Use automatic camera positioning while in edit mode" - top_delta="0" width="242" /> - <text + <text type="string" length="1" - follows="left|top" - height="12" - layout="topleft" - left="10" - name="text2" - top_pad="5" - width="128"> - Avatar Display Options: - </text> + height="10" + left="30" + text_color="EmphasisColor" + name="text_box1" + top_pad="30" + width="270"> +My Avatar: + </text> <check_box control_name="FirstPersonAvatarVisible" - height="16" - label="Show Avatar in Mouselook" + height="20" + label="Show me in Mouselook" layout="topleft" - left_pad="10" name="first_person_avatar_visible" - top_delta="0" width="256" /> - - <check_box - control_name="UseChatBubbles" - height="16" - label="Show chat bubbles" + <check_box + control_name="ArrowKeysMoveAvatar" + height="20" + label="Arrow keys always move me" layout="topleft" left="30" - name="bubble_text_chat" - top_pad="5" - width="400" /> - - - + name="arrow_keys_move_avatar_check" + width="237" /> </panel>
\ No newline at end of file diff --git a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml index 0c01aec95d..ecfb0048a3 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_graphics1.xml @@ -9,14 +9,6 @@ name="Display panel" top="1" width="517"> - <panel.string - name="resolution_format"> - [RES_X] x [RES_Y] - </panel.string> - <panel.string - name="aspect_ratio_text"> - [NUM]:[DEN] - </panel.string> <text type="string" length="1" @@ -39,63 +31,63 @@ top_pad="4" width="175"> </check_box> - <combo_box + <combo_box visiblity_control="WindowFullScreen" - allow_text_entry="false" - enabled="true" - layout="topleft" - height="18" - left_delta="220" - max_chars="20" - mouse_opaque="true" - name="windowsize combo" - top_delta="-1" - width="150"> - <combo_box.item - type="string" - length="1" - enabled="true" - name="640x480" - value="640 x 480"> - 640x480 - </combo_box.item> - <combo_box.item - type="string" - length="1" - enabled="true" - name="800x600" - value="800 x 600"> - 800x600 - </combo_box.item> - <combo_box.item - type="string" - length="1" - enabled="true" - name="720x480" - value="720 x 480"> - 720x480 (NTSC) - </combo_box.item> - <combo_box.item - type="string" - length="1" - enabled="true" - name="768x576" - value="768 x 576"> - 768x576 (PAL) - </combo_box.item> - <combo_box.item - type="string" - length="1" - enabled="true" - name="1024x768" - value="1024 x 768"> - 1024x768 - </combo_box.item> - <combo_box.commit_callback - function="setControlFalse" - parameter="FullScreenAutoDetectAspectRatio" /> + allow_text_entry="false" + enabled="true" + layout="topleft" + height="18" + left_delta="220" + max_chars="20" + mouse_opaque="true" + name="windowsize combo" + top_delta="-1" + width="150"> + <combo_box.item + type="string" + length="1" + enabled="true" + name="640x480" + value="640 x 480"> + 640x480 + </combo_box.item> + <combo_box.item + type="string" + length="1" + enabled="true" + name="800x600" + value="800 x 600"> + 800x600 + </combo_box.item> + <combo_box.item + type="string" + length="1" + enabled="true" + name="720x480" + value="720 x 480"> + 720x480 (NTSC) + </combo_box.item> + <combo_box.item + type="string" + length="1" + enabled="true" + name="768x576" + value="768 x 576"> + 768x576 (PAL) + </combo_box.item> + <combo_box.item + type="string" + length="1" + enabled="true" + name="1024x768" + value="1024 x 768"> + 1024x768 + </combo_box.item> + <combo_box.commit_callback + function="Pref.setControlFalse" + parameter="FullScreenAutoDetectAspectRatio" /> </combo_box> - <text + <text type="string" length="1" follows="left|top" @@ -134,7 +126,6 @@ name="ui_scale_slider" top_delta="0" width="58" /> - <text type="string" length="1" diff --git a/indra/newview/skins/default/xui/en/panel_profile.xml b/indra/newview/skins/default/xui/en/panel_profile.xml index 2a49db8d4f..5d0af26af5 100644 --- a/indra/newview/skins/default/xui/en/panel_profile.xml +++ b/indra/newview/skins/default/xui/en/panel_profile.xml @@ -1,494 +1,379 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - bevel_style="in" + bevel_style="out" follows="left|top|right|bottom" - height="570" - width="295" - border="false" + height="420" + label="Profile" layout="topleft" left="0" + name="panel_profile" top="0" - label="Profile" - mouse_opaque="false" - name="panel_profile"> - <panel.string + width="255"> + <string name="CaptionTextAcctInfo"> [ACCTTYPE] [PAYMENTINFO] [AGEVERIFICATION] - </panel.string> - <panel.string - name="AcctTypeResident"> - Resident - </panel.string> - <panel.string - name="AcctTypeTrial"> - Trial - </panel.string> - <panel.string - name="AcctTypeCharterMember"> - Charter Member - </panel.string> - <panel.string - name="AcctTypeEmployee"> - Linden Lab Employee - </panel.string> - <panel.string - name="PaymentInfoUsed"> - Payment Info Used - </panel.string> - <panel.string - name="PaymentInfoOnFile"> - Payment Info On File - </panel.string> - <panel.string - name="NoPaymentInfoOnFile"> - No Payment Info On File - </panel.string> - <panel.string - name="AgeVerified"> - Age-verified - </panel.string> - <panel.string - name="NotAgeVerified"> - Not Age-verified - </panel.string> - <panel.string + </string> + <string + name="AcctTypeResident" + value="Resident" /> + <string + name="AcctTypeTrial" + value="Trial" /> + <string + name="AcctTypeCharterMember" + value="Charter Member" /> + <string + name="AcctTypeEmployee" + value="Linden Lab Employee" /> + <string + name="PaymentInfoUsed" + value="Payment Info Used" /> + <string + name="PaymentInfoOnFile" + value="Payment Info On File" /> + <string + name="NoPaymentInfoOnFile" + value="No Payment Info On File" /> + <string + name="AgeVerified" + value="Age-verified" /> + <string + name="NotAgeVerified" + value="Not Age-verified" /> + <string name="payment_update_link_url"> http://www.secondlife.com/account/billing.php?lang=en - </panel.string> - <panel.string - name="my_account_link_url"> - http://secondlife.com/account - </panel.string> - <panel.string + </string> + <string + name="my_account_link_url" + value="http://secondlife.com/account" /> + <string name="partner_edit_link_url"> http://www.secondlife.com/account/partners.php?lang=en - </panel.string> - <panel.string - name="no_partner_text"> - None - </panel.string> + </string> + <string + name="no_partner_text" + value="None" /> <scroll_container color="DkGray2" follows="left|top|right|bottom" - height="570" - width="265" + height="300" layout="topleft" - left="5" - top_pad="0" - border="false" - opaque="true" + left="0" name="profile_scroll" - reserve_scroll_corner="false"> - <panel - height="300" + reserve_scroll_corner="true" + opaque="true" + width="255"> + <panel + name="scroll_content_panel" + follows="left|top|right" + layout="topleft" + top="0" + left="0" + width="240" + height="520"> + + <panel + follows="left|top" + height="125" layout="topleft" - left="0" - top="0" - bevel_style="in" - width="265"> - <panel - follows="left|top" - height="100" - layout="topleft" - left="10" - bevel_style="in" - name="second_life_image_panel" - top="20" - width="230"> - <texture_picker - allow_no_texture="true" - default_image_name="None" - enabled="false" - follows="top|left" - height="125" - layout="topleft" - left="0" - name="2nd_life_pic" - top="0" - width="125" /> - <text - type="string" - follows="left|top" - font="SansSerifBold" - height="15" + left="10" + name="second_life_image_panel" + top="10" + width="260"> + <texture_picker + allow_no_texture="true" + default_image_name="None" + enabled="false" + follows="top|left" + height="125" layout="topleft" - left_pad="10" - name="title_sl_descr_text" - text_color="white" - top_delta="0" - width="125"> - Second Life: - </text> + left="0" + name="2nd_life_pic" + top="10" + width="125" /> <text - type="string" - left_pad="10" - top_pad="5" follows="left|top|right" - font="SansSerifSmall" - height="125" - layout="topleft" - name="sl_description_edit" - text_color="LtGray" - width="125" - word_wrap="true"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. - </text> - </panel> - <panel - follows="left|top" - height="100" - layout="topleft" - left="10" - name="first_life_image_panel" - top_pad="20" - width="125"> - <texture_picker - allow_no_texture="true" - default_image_name="None" - enabled="false" - follows="top|left" - height="125" - layout="topleft" - left="0" - name="real_world_pic" - top_pad="0" - width="125" /> - <text - type="string" - follows="left|top" font="SansSerifBold" height="15" layout="topleft" left_pad="10" - name="title_rw_descr_text" + name="title_sl_descr_text" text_color="white" top_delta="0" - width="125"> - Real World: - </text> + value="[SECOND_LIFE]:" + width="140" /> <text - type="string" follows="left|top|right" - font="SansSerifSmall" - height="125" + height="90" layout="topleft" - left_pad="10" - top_pad="10" - name="fl_description_edit" - text_color="LtGray" - width="125" + name="sl_description_edit" + width="130" word_wrap="true"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. </text> - </panel> - <link - type="string" - follows="left|top|right" - font="SansSerifSmall" - height="15" - top_pad="20" - layout="topleft" - left="10" - name="homepage_edit" - text_color="HTMLLinkColor" - hover_color="0.5 0.4 1 1" - width="280"> - TODO - </link> - <text - type="string" - follows="left|top" - font="SansSerifBold" - height="15" - halign="right" + </panel> + <panel + follows="left|top" + height="125" + layout="topleft" + left="10" + name="first_life_image_panel" + width="260"> + <texture_picker + allow_no_texture="true" + default_image_name="None" + enabled="false" + follows="top|left" + height="125" layout="topleft" - left="10" - name="title_member_text" - text_color="white" - top_pad="20" - width="100"> - Member since: - </text> + left="0" + name="real_world_pic" + width="125" /> <text - type="string" follows="left|top|right" - font="SansSerifSmall" - height="15" - layout="topleft" - left_pad="10" - top_delta="0" - name="register_date" - text_color="LtGray" - width="125" - word_wrap="true"> - 05/31/1976 - </text> - <text - type="string" - follows="left|top" font="SansSerifBold" height="15" - halign="right" - layout="topleft" - left="10" - top_pad="20" - name="title_acc_status_text" - text_color="white" - width="100"> - Status: - </text> - <text - type="string" - follows="left|top|right" - font="SansSerifSmall" - height="30" layout="topleft" left_pad="10" - top_delta="0" - name="acc_status_text" - text_color="LtGray" - width="125" - word_wrap="true"> - Resident. No payment info on file. - </text> - <text - type="string" - follows="left|top" - font="SansSerifBold" - height="15" - halign="right" - layout="topleft" - name="title_partner_text" + name="title_rw_descr_text" text_color="white" - top_pad="20" - left="10" - width="100"> - Partner: - </text> - <panel - follows="left|top|right" - height="15" - layout="topleft" - left_pad="10" top_delta="0" - name="partner_data_panel" - width="125"> - <text - type="string" - follows="left|top|right" - font="SansSerifSmall" - height="15" - layout="topleft" - left="0" - name="partner_text" - text_color="LtGray" - top="0" - width="125" - word_wrap="true"> - [FIRST] [LAST] - </text> - <link - type="string" - follows="top|right" - font="SansSerifSmall" - height="15" - layout="topleft" - left_delta="0" - name="partner_edit_link" - text_color="HTMLLinkColor" - hover_color="0.5 0.4 1 1" - top_delta="15" - width="40"> - Edit - </link> - </panel> + value="Real World:" + width="140" /> <text - type="string" - follows="left|top" - font="SansSerifBold" - height="15" - layout="topleft" - halign="right" - left="10" - name="title_groups_text" - text_color="white" - top_pad="20" - width="100"> - Groups: - </text> - <text - type="string" - follows="left|top|right|bottom" - font="SansSerifSmall" - height="120" + follows="left|top|right" + height="90" layout="topleft" - left_pad="10" - top_delta="0" - name="sl_groups" - text_color="LtGray" - width="125" + name="fl_description_edit" + width="130" word_wrap="true"> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. + Lorem ipsum dolor sit amet, consectetur adlkjpiscing elit moose moose. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet. adipiscing elit. Aenean rigviverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet sorbet ipsum. adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. </text> - <!--panel - follows="left|top|right" - height="15" - layout="topleft" - left="50" - top_pad="30" - name="account_actions_panel" - width="200"> - <link - type="string" - follows="left|top" - font="SansSerif" - height="15" - layout="topleft" - left="0" - name="payment_update_link" - text_color="HTMLLinkColor" - hover_color="0.5 0.4 1 1" - top="0" - width="100"> - Update - </link> - <link - follows="left|top" - font="SansSerif" - height="15" - layout="topleft" - left="70" - name="my_account_link" - text_color="HTMLLinkColor" - hover_color="0.5 0.4 1 1" - top="0" - width="80"> - My Account - </link> - </panel--> - - - - <!-- - <panel + </panel> + <link + follows="left|top|right" + height="15" + hover_color="0.5 0.4 1 1" + layout="topleft" + left="10" + name="homepage_edit" + text_color="HTMLLinkColor" + top_pad="5" + value="http://librarianavengers.org" + width="265" + word_wrap="true" /> + <text + follows="left|top" + font="SansSerifBold" + halign="right" + height="15" + layout="topleft" + left="10" + name="title_member_text" + text_color="white" + top_pad="10" + value="Member since:" + width="100" /> + <text + follows="left|top|right" + height="15" + layout="topleft" + left_pad="10" + name="register_date" + top_delta="0" + value="05/31/1976" + width="160" + word_wrap="true" /> + <text + follows="left|top" + font="SansSerifBold" + halign="right" + height="15" + layout="topleft" + left="10" + name="title_acc_status_text" + text_color="white" + top_pad="20" + value="Status:" + width="100" /> + <text + follows="left|top|right" + height="30" + layout="topleft" + left_pad="10" + name="acc_status_text" + top_delta="0" + value="Resident. No payment info on file." + width="160" + word_wrap="true" /> + <spinner + control_name="AFKTimeout" + decimal_digits="0" + follows="left|top" + height="16" + increment="1" + initial_value="300" + label="Away Timeout:" + label_width="141" + layout="topleft" + left="10" + max_val="600" + min_val="30" + name="afk_timeout_spinner" + top_pad="10" + width="202" /> + <text + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="220" + name="seconds_textbox" + top_delta="0" + width="128"> + seconds + </text> + <text + type="string" + length="1" + follows="left|top" + height="10" + layout="topleft" + left="12" + mouse_opaque="false" + name="text_box3" + top_pad="10" + width="128"> + Busy Mode Response: + </text> + <text_editor + control_name="BusyModeResponse2" + commit_on_focus_lost = "true" + follows="left|top" + height="70" + layout="topleft" + left_pad="8" + name="busy_response" + top_delta="0" + width="330" + word_wrap="true"> + log_in_to_change + </text_editor> + <text + follows="left|top" + font="SansSerifBold" + halign="right" + height="15" + layout="topleft" + left="10" + name="title_partner_text" + text_color="white" + top_pad="20" + value="Partner:" + width="100" /> + <panel + follows="left|top|right" + height="50" + layout="topleft" + left_pad="10" + name="partner_data_panel" + top_delta="0" + width="125"> + <text follows="left|top|right" height="30" layout="topleft" left="0" - name="status_panel" - top="155" - width="260"> - <text - follows="left|top" - font="SansSerifBold" - font.style="ITALIC" - height="15" - layout="topleft" - left="9" - name="online_status" - text_color="green" - width="100" /> - <text - type="string" - follows="left|top" - font="SansSerifBold" - font.style="ITALIC" - height="15" - layout="topleft" - left="9" - name="status_message" - text_color="0.1 0.1 0.1 1" - width="280"> - TBD - </text> - </panel> --> - <!--panel - follows="left|top|right" - height="80" + name="partner_text" + top="0" + value="[FIRST] [LAST]" + width="160" + word_wrap="true" /> + <link + follows="top|right" + height="15" + hover_color="0.5 0.4 1 1" layout="topleft" - left="0" - name="status_me_panel" - top_pad="20" - width="125"> - <text - type="string" - follows="left|top" - font="SansSerifBold" - height="15" - halign="right" - layout="topleft" - left="10" - name="online_me_status_text" - text_color="white" - width="100"> - Status: - </text> - <combo_box - font="SansSerifBold" - height="18" - layout="topleft" - left_pad="10" - top_delta="0" - name="status_combo" - width="100"> - <combo_box.item - label="Online" - name="online" - value="online" /> - <combo_box.item - label="Away" - name="away" - value="away" /> - <combo_box.item - label="Busy" - name="busy" - value="busy" /> - </combo_box> - <text - type="string" - follows="left|top" - font="SansSerifBold" - height="15" - layout="topleft" - left="10" - top_pad="20" - name="status_me_message_text" - text_color="white" - width="250"> - What're you up to? - </text> - <line_editor - border_style="line" - border_thickness="1" - follows="left|top|right" - font="SansSerifBold" - font.style="ITALIC" - height="20" - layout="topleft" - left="10" - top_pad="10" - name="status_me_message_edit" - select_on_focus="true" - text_color="0.5 0.5 0.5 1" - width="230"> - Type a message about what you're doing in SL! - </line_editor> - </panel--> - - + left_delta="0" + name="partner_edit_link" + text_color="HTMLLinkColor" + top_delta="15" + value="Edit" + width="40" /> + </panel> + <text + type="string" + follows="left|top" + font="SansSerifBold" + height="15" + halign="right" + layout="topleft" + left="10" + name="online_me_status_text" + text_color="white" + top_pad="20" + width="100"> + Status: + </text> + <combo_box + font="SansSerifBold" + height="18" + layout="topleft" + left_pad="0" + top_delta="0" + name="status_combo" + width="100"> + <combo_box.item + label="Online" + name="online" + value="online" /> + <combo_box.item + label="Away" + name="away" + value="away" /> + <combo_box.item + label="Busy" + name="busy" + value="busy" /> + </combo_box> + <text + follows="left|top" + font="SansSerifBold" + halign="right" + height="15" + layout="topleft" + left="10" + name="title_groups_text" + text_color="white" + top_pad="20" + value="Groups:" + width="100" /> + <text + follows="left|top|right|bottom" + height="160" + layout="topleft" + left_pad="10" + name="sl_groups" + top_delta="0" + width="130" + word_wrap="true"> + Lorem ipsum dolor sit amet, consectetur adlkjpiscing elit moose moose. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet. adipiscing elit. Aenean rigviverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet sorbet ipsum. adipiscing elit. Aenean viverra orci et justo sagittis aliquet. Nullam malesuada mauris sit amet ipsum. + </text> - </panel> + </panel> </scroll_container> <panel follows="bottom|left" height="30" - width="280" layout="topleft" left="10" + name="profile_buttons_panel" top_pad="5" - name="profile_buttons_panel"> + width="280"> <button follows="bottom|left" font="SansSerifSmallBold" @@ -506,9 +391,9 @@ height="25" label="IM" layout="topleft" - left_pad="0" name="im" top="0" + left_pad="0" width="40" /> <button enabled="false" @@ -517,8 +402,8 @@ height="25" label="Call" layout="topleft" - left_pad="0" name="call" + left_pad="0" top="0" width="50" /> <button @@ -527,31 +412,19 @@ height="25" label="Teleport" layout="topleft" - left_pad="0" name="teleport" - top="0" - width="75" /> - <!-- <button - enabled="false" - follows="bottom|left" - font="SansSerifSmallBold" - height="25" - label="Share" - layout="topleft" left_pad="0" - name="share" top="0" - width="60" />--> + width="75" /> </panel> - <panel - follows="bottom|left" + <panel height="30" - width="280" layout="topleft" left="10" + name="profile_me_buttons_panel" top_pad="5" visible="false" - name="profile_me_buttons_panel"> + width="280"> <button follows="bottom|left" font="SansSerifSmallBold" diff --git a/indra/newview/skins/default/xui/en/panel_profile_view.xml b/indra/newview/skins/default/xui/en/panel_profile_view.xml index a14d8bbc6b..e710774232 100644 --- a/indra/newview/skins/default/xui/en/panel_profile_view.xml +++ b/indra/newview/skins/default/xui/en/panel_profile_view.xml @@ -1,77 +1,92 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel background_visible="true" - follows="left|top|right|bottom" - width="305" - height="650" - label="Profile" - bevel_style="in" + follows="all" + height="550" layout="topleft" - left="0" + min_height="350" + min_width="240" name="panel_target_profile" - top="0"> + width="305"> <text + follows="top|left|right" + font="SansSerifHugeBold" + height="20" layout="topleft" - top="0" left="10" - width="250" - height="20" - font="SansSerifHugeBold" + name="user_name" text_color="white" - follows="top|left|right" - mouse_opaque="true" - name="user_name">(Loading...)</text> + top="0" + value="(Loading...)" + width="250" /> <text - layout="topleft" - width="100" + follows="top|left" height="16" + layout="topleft" left="10" - top_pad="5" - font="SansSerifTiny" + name="status" text_color="LtGray_50" - follows="top|left" - mouse_opaque="true" - name="status">Online</text> + top_pad="5" + value="Online" + width="100" /> <button + follows="top|right" + height="25" + image_overlay="BackArrow_Off" layout="topleft" name="back" + picture_style="true" right="-10" - top="0" - width="25" - height="25" - label="" tab_stop="false" - follows="top|right" - image_selected="" - image_unselected="" - image_overlay="BackArrow_Off" /> - <tab_container - follows="left|top|right|bottom" - height="550" - width="306" - tab_min_width="75" - layout="topleft" - left="10" - name="tabs" - tab_position="top" - top_pad="20"> - <panel - class="panel_profile" - filename="panel_profile.xml" - label="Profile" - layout="topleft" - name="panel_profile" /> - <panel - class="panel_picks" - filename="panel_picks.xml" - label="Picks" - layout="topleft" - name="panel_picks" /> - <panel - class="panel_notes" - filename="panel_notes.xml" - label="Notes & Privacy" + top="0" + width="25" /> + <layout_stack + name="layout_stack" + orientation="vertical" layout="topleft" - name="panel_notes" /> - </tab_container> + follows="all" + left="0" + top="50" + height="500" + width="305" + border_size="0"> + <panel + name="stack_panel" + follows="all" + layout="topleft" + top="0" + left="0" + height="500" + width="305"> + <tab_container + follows="all" + height="500" + layout="topleft" + left="10" + name="tabs" + tab_min_width="75" + tab_position="top" + top="0" + width="280"> + <panel + class="panel_profile" + filename="panel_profile.xml" + label="Profile" + layout="topleft" + name="panel_profile" /> + <panel + class="panel_picks" + filename="panel_picks.xml" + label="Picks" + layout="topleft" + name="panel_picks" /> + <panel + class="panel_notes" + filename="panel_notes.xml" + label="Notes & Privacy" + layout="topleft" + name="panel_notes" /> + </tab_container> + </panel> + </layout_stack> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_region_covenant.xml b/indra/newview/skins/default/xui/en/panel_region_covenant.xml index 8265ab3227..8445b933bf 100644 --- a/indra/newview/skins/default/xui/en/panel_region_covenant.xml +++ b/indra/newview/skins/default/xui/en/panel_region_covenant.xml @@ -140,6 +140,7 @@ max_length="65535" name="covenant_editor" top_delta="30" + handle_edit_keys_directly="true" width="193" word_wrap="true"> There is no Covenant provided for this Estate. diff --git a/indra/newview/skins/default/xui/en/panel_region_estate.xml b/indra/newview/skins/default/xui/en/panel_region_estate.xml index 1dafd50cdb..ee52184119 100644 --- a/indra/newview/skins/default/xui/en/panel_region_estate.xml +++ b/indra/newview/skins/default/xui/en/panel_region_estate.xml @@ -2,517 +2,529 @@ <panel border="true" follows="top|left" - height="950" + height="512" + name="EstateWrapper" label="Estate" layout="topleft" - left="0" - name="Estate" - top="320" width="280"> - <panel.string - name="email_unsupported"> - Feature unsupported - </panel.string> - <text - type="string" - length="1" - follows="left|top" - height="32" + <scroll_container + follows="top|left|right|bottom" + height="508" + layout="topleft" + width="280"> + <panel + follows="top|left" + height="950" + label="Estate" layout="topleft" - left="10" - name="estate_help_text" - top="14" - width="300"> + name="Estate" + width="258"> + <panel.string + name="email_unsupported"> + Feature unsupported + </panel.string> + <text + type="string" + length="1" + follows="left|top" + height="32" + layout="topleft" + left="10" + name="estate_help_text" + top="14" + width="300"> Changes to settings on this tab will affect all -regions in the estate. - </text> - <text - type="string" - length="1" - follows="left|top" - font="SansSerif" - height="20" - layout="topleft" - left_delta="0" - name="estate_text" - top_pad="2" - width="80"> + regions in the estate. + </text> + <text + type="string" + length="1" + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left_delta="0" + name="estate_text" + top_pad="2" + width="80"> Estate: - </text> - <text - type="string" - length="1" - follows="left|top" - font="SansSerif" - height="20" - layout="topleft" - left_delta="0" - name="estate_name" - top_delta="16" - width="150"> + </text> + <text + type="string" + length="1" + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left_delta="0" + name="estate_name" + top_delta="16" + width="150"> (unknown) - </text> - <text - type="string" - length="1" - follows="left|top" - font="SansSerif" - height="20" - layout="topleft" - left_delta="0" - name="owner_text" - top_pad="2" - width="80"> + </text> + <text + type="string" + length="1" + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left_delta="0" + name="owner_text" + top_pad="2" + width="80"> Owner: - </text> - <text - type="string" - length="1" - follows="left|top" - font="SansSerif" - height="20" - layout="topleft" - left_delta="0" - name="estate_owner" - top_delta="16" - width="150"> + </text> + <text + type="string" + length="1" + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left_delta="0" + name="estate_owner" + top_delta="16" + width="150"> (unknown) - </text> - <view_border - bevel_style="in" - follows="top|left" - height="290" - layout="topleft" - left_delta="-4" - top_pad="5" - width="250" /> - <check_box - height="20" - label="Use Global Time" - layout="topleft" - left="12" - name="use_global_time_check" - top="132" - width="200" /> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_pad="20" - name="use_global_time_help" - top_delta="2" - width="18" /> - <check_box - height="20" - label="Fixed Sun" - layout="topleft" - left="12" - name="fixed_sun_check" - top="152" - width="100" /> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_pad="120" - name="fixed_sun_help" - top_delta="2" - width="18" /> - <icon - height="20" - image_name="icon_day_cycle.tga" - layout="topleft" - left="47" - name="daycycle" - top="177" - width="165" /> - <slider - follows="left|top" - height="20" - increment="0.001" - label="Phase" - layout="topleft" - left="12" - max_val="30" - min_val="6" - name="sun_hour_slider" - show_text="false" - top="202" - width="200" /> - <check_box - height="20" - label="Allow Public Access" - layout="topleft" - left_delta="0" - name="externally_visible_check" - top_pad="6" - width="200" /> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_pad="20" - name="externally_visible_help" - top_delta="2" - width="18" /> - <text - type="string" - length="1" - follows="top|left" - height="16" - layout="topleft" - left="32" - name="Only Allow" - top="250" - width="278"> + </text> + <view_border + bevel_style="in" + follows="top|left" + height="290" + layout="topleft" + left_delta="-4" + top_pad="5" + width="250" /> + <check_box + height="20" + label="Use Global Time" + layout="topleft" + left="12" + name="use_global_time_check" + top="132" + width="200" /> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_pad="20" + name="use_global_time_help" + top_delta="2" + width="18" /> + <check_box + height="20" + label="Fixed Sun" + layout="topleft" + left="12" + name="fixed_sun_check" + top="152" + width="100" /> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_pad="120" + name="fixed_sun_help" + top_delta="2" + width="18" /> + <icon + height="20" + image_name="icon_day_cycle.tga" + layout="topleft" + left="47" + name="daycycle" + top="177" + width="165" /> + <slider + follows="left|top" + height="20" + increment="0.001" + label="Phase" + layout="topleft" + left="12" + max_val="30" + min_val="6" + name="sun_hour_slider" + show_text="false" + top="202" + width="200" /> + <check_box + height="20" + label="Allow Public Access" + layout="topleft" + left_delta="0" + name="externally_visible_check" + top_pad="6" + width="200" /> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_pad="20" + name="externally_visible_help" + top_delta="2" + width="18" /> + <text + type="string" + length="1" + follows="top|left" + height="16" + layout="topleft" + left="32" + name="Only Allow" + top="250" + width="278"> Restrict Access To: - </text> - <check_box - follows="top|left" - height="16" - label="Residents with payment info on file" - layout="topleft" - left_delta="0" - name="limit_payment" - tool_tip="Ban unidentified residents." - top_pad="2" - width="278" /> - <check_box - follows="top|left" - height="16" - label="Age-verified adults" - layout="topleft" - left_delta="0" - name="limit_age_verified" - tool_tip="Ban residents who have not verified their age. See support.secondlife.com for more information." - top_pad="2" - width="278" /> - <check_box - height="20" - label="Allow Voice Chat" - layout="topleft" - left="12" - name="voice_chat_check" - top="304" - width="200" /> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_pad="20" - name="voice_chat_help" - top_delta="2" - width="18" /> - <check_box - height="20" - label="Allow Direct Teleport" - layout="topleft" - left="12" - name="allow_direct_teleport" - top_pad="4" - width="80" /> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_pad="140" - name="allow_direct_teleport_help" - top_delta="2" - width="18" /> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="10" - name="abuse_email_text" - top_pad="5" - width="180"> + </text> + <check_box + follows="top|left" + height="16" + label="Residents with payment info on file" + layout="topleft" + left_delta="0" + name="limit_payment" + tool_tip="Ban unidentified residents." + top_pad="2" + width="278" /> + <check_box + follows="top|left" + height="16" + label="Age-verified adults" + layout="topleft" + left_delta="0" + name="limit_age_verified" + tool_tip="Ban residents who have not verified their age. See the [SUPPORT_SITE] for more information." + top_pad="2" + width="278" /> + <check_box + height="20" + label="Allow Voice Chat" + layout="topleft" + left="12" + name="voice_chat_check" + top="304" + width="200" /> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_pad="20" + name="voice_chat_help" + top_delta="2" + width="18" /> + <check_box + height="20" + label="Allow Direct Teleport" + layout="topleft" + left="12" + name="allow_direct_teleport" + top_pad="4" + width="80" /> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_pad="140" + name="allow_direct_teleport_help" + top_delta="2" + width="18" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="abuse_email_text" + top_pad="5" + width="180"> Abuse email address: - </text> - <line_editor - follows="top|left" - height="19" - layout="topleft" - left="15" - name="abuse_email_address" - top_pad="5" - width="205" /> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_pad="12" - name="abuse_email_address_help" - top_dekta="0" - width="18" /> - <button - enabled="false" - follows="left|top" - height="20" - label="Apply" - layout="topleft" - name="apply_btn" - right="250" - top_pad="4" - width="90" /> - <button - follows="left|top" - height="20" - label="Send Message To Estate..." - layout="topleft" - left="8" - name="message_estate_btn" - top_pad="5" - width="250" /> - <button - follows="left|top" - height="20" - label="Kick User from Estate..." - layout="topleft" - left="8" - name="kick_user_from_estate_btn" - top_pad="5" - width="250" /> - <text - type="string" - length="1" - top="490" - follows="left|top" - height="20" - layout="topleft" - name="estate_manager_label" - left="8" - width="200"> + </text> + <line_editor + follows="top|left" + height="19" + layout="topleft" + left="15" + name="abuse_email_address" + top_pad="5" + width="205" /> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_pad="12" + name="abuse_email_address_help" + top_delta="0" + width="18" /> + <button + enabled="false" + follows="left|top" + height="20" + label="Apply" + layout="topleft" + name="apply_btn" + right="250" + top_pad="4" + width="90" /> + <button + follows="left|top" + height="20" + label="Send Message To Estate..." + layout="topleft" + left="8" + name="message_estate_btn" + top_pad="5" + width="250" /> + <button + follows="left|top" + height="20" + label="Kick User from Estate..." + layout="topleft" + left="8" + name="kick_user_from_estate_btn" + top_pad="5" + width="250" /> + <text + type="string" + length="1" + top="490" + follows="left|top" + height="20" + layout="topleft" + name="estate_manager_label" + left="8" + width="200"> Estate Managers: - </text> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_delta="232" - name="estate_manager_help" - top_delta="-1" - width="18" /> - <view_border - bevel_style="none" - follows="top|left" - height="60" - layout="topleft" - left="8" - top_pad="5" - width="250" /> - <name_list - follows="left|top" - height="60" - layout="topleft" - left_delta="0" - multi_select="true" - name="estate_manager_name_list" - top_delta="0" - width="250" /> - <button - follows="left|top" - height="20" - label="Add..." - layout="topleft" - left="8" - name="add_estate_manager_btn" - top_pad="5" - width="90" /> - <button - follows="left|top" - height="20" - label="Remove..." - layout="topleft" - name="remove_estate_manager_btn" - left_pad="70" - top_delta="0" - width="90" /> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="8" - name="allow_resident_label" - top_pad="5" - width="200"> + </text> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_delta="232" + name="estate_manager_help" + top_delta="-1" + width="18" /> + <view_border + bevel_style="none" + follows="top|left" + height="60" + layout="topleft" + left="8" + top_pad="5" + width="250" /> + <name_list + follows="left|top" + height="60" + layout="topleft" + left_delta="0" + multi_select="true" + name="estate_manager_name_list" + top_delta="0" + width="250" /> + <button + follows="left|top" + height="20" + label="Add..." + layout="topleft" + left="8" + name="add_estate_manager_btn" + top_pad="5" + width="90" /> + <button + follows="left|top" + height="20" + label="Remove..." + layout="topleft" + name="remove_estate_manager_btn" + left_pad="70" + top_delta="0" + width="90" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="8" + name="allow_resident_label" + top_pad="5" + width="200"> Allowed Residents: - </text> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_delta="232" - name="allow_resident_help" - top_delta="-1" - width="18" /> - <view_border - bevel_style="none" - follows="top|left" - height="60" - layout="topleft" - left="8" - top_pad="5" - width="250" /> - <name_list - follows="left|top" - height="60" - layout="topleft" - left_delta="0" - multi_select="true" - name="allowed_avatar_name_list" - top_delta="0" - width="250" /> - <button - follows="left|top" - height="20" - label="Add..." - layout="topleft" - left="8" - name="add_allowed_avatar_btn" - top_pad="5" - width="90" /> - <button - follows="left|top" - height="20" - label="Remove..." - layout="topleft" - name="remove_allowed_avatar_btn" - left_pad="70" - top_delta="0" - width="90" /> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="8" - name="allow_group_label" - top_pad="5" - width="200"> + </text> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_delta="232" + name="allow_resident_help" + top_delta="-1" + width="18" /> + <view_border + bevel_style="none" + follows="top|left" + height="60" + layout="topleft" + left="8" + top_pad="5" + width="250" /> + <name_list + follows="left|top" + height="60" + layout="topleft" + left_delta="0" + multi_select="true" + name="allowed_avatar_name_list" + top_delta="0" + width="250" /> + <button + follows="left|top" + height="20" + label="Add..." + layout="topleft" + left="8" + name="add_allowed_avatar_btn" + top_pad="5" + width="90" /> + <button + follows="left|top" + height="20" + label="Remove..." + layout="topleft" + name="remove_allowed_avatar_btn" + left_pad="70" + top_delta="0" + width="90" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="8" + name="allow_group_label" + top_pad="5" + width="200"> Allowed Groups: - </text> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_delta="232" - name="allow_group_help" - top_delta="-1" - width="18" /> - <view_border - bevel_style="none" - follows="top|left" - height="60" - layout="topleft" - left="8" - top_pad="5" - width="250" /> - <name_list - follows="left|top" - height="60" - layout="topleft" - left_delta="0" - multi_select="true" - name="allowed_group_name_list" - top_delta="0" - width="250" /> - <button - follows="left|top" - height="20" - label="Add..." - layout="topleft" - left="8" - name="add_allowed_group_btn" - top_pad="5" - width="90" /> - <button - follows="left|top" - height="20" - label="Remove..." - layout="topleft" - name="remove_allowed_group_btn" - left_pad="70" - top_delta="0" - width="90" /> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="8" - name="ban_resident_label" - top_pad="5" - width="200"> + </text> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_delta="232" + name="allow_group_help" + top_delta="-1" + width="18" /> + <view_border + bevel_style="none" + follows="top|left" + height="60" + layout="topleft" + left="8" + top_pad="5" + width="250" /> + <name_list + follows="left|top" + height="60" + layout="topleft" + left_delta="0" + multi_select="true" + name="allowed_group_name_list" + top_delta="0" + width="250" /> + <button + follows="left|top" + height="20" + label="Add..." + layout="topleft" + left="8" + name="add_allowed_group_btn" + top_pad="5" + width="90" /> + <button + follows="left|top" + height="20" + label="Remove..." + layout="topleft" + name="remove_allowed_group_btn" + left_pad="70" + top_delta="0" + width="90" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="8" + name="ban_resident_label" + top_pad="5" + width="200"> Banned Residents: - </text> - <button - follows="left|top" - font="SansSerifSmall" - height="18" - label="?" - layout="topleft" - left_delta="232" - name="ban_resident_help" - top_delta="-1" - width="18" /> - <view_border - bevel_style="none" - follows="top|left" - height="60" - layout="topleft" - left="8" - top_pad="5" - width="250" /> - <name_list - follows="left|top" - height="60" - layout="topleft" - left_delta="0" - multi_select="true" - name="banned_avatar_name_list" - top_delta="0" - width="250" /> - <button - follows="left|top" - height="20" - label="Add..." - layout="topleft" - left="8" - name="add_banned_avatar_btn" - top_pad="5" - width="90" /> - <button - follows="left|top" - height="20" - label="Remove..." - layout="topleft" - name="remove_banned_avatar_btn" - left_pad="70" - top_delta="0" - width="90" /> + </text> + <button + follows="left|top" + font="SansSerifSmall" + height="18" + label="?" + layout="topleft" + left_delta="232" + name="ban_resident_help" + top_delta="-1" + width="18" /> + <view_border + bevel_style="none" + follows="top|left" + height="60" + layout="topleft" + left="8" + top_pad="5" + width="250" /> + <name_list + follows="left|top" + height="60" + layout="topleft" + left_delta="0" + multi_select="true" + name="banned_avatar_name_list" + top_delta="0" + width="250" /> + <button + follows="left|top" + height="20" + label="Add..." + layout="topleft" + left="8" + name="add_banned_avatar_btn" + top_pad="5" + width="90" /> + <button + follows="left|top" + height="20" + label="Remove..." + layout="topleft" + name="remove_banned_avatar_btn" + left_pad="70" + top_delta="0" + width="90" /> + </panel> + </scroll_container> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_region_texture.xml b/indra/newview/skins/default/xui/en/panel_region_texture.xml index bf63bb0cb6..8e8fc9ef7f 100644 --- a/indra/newview/skins/default/xui/en/panel_region_texture.xml +++ b/indra/newview/skins/default/xui/en/panel_region_texture.xml @@ -2,333 +2,344 @@ <panel border="true" follows="top|left" - height="575" + height="512" label="Ground Textures" layout="topleft" - left="0" - name="Textures" - top="320" width="280"> - <text - type="string" - length="1" - follows="left|top" - font="SansSerif" - height="20" + <scroll_container + follows="top|left|right|bottom" + height="508" + layout="topleft" + width="280"> + <panel + follows="top|left" + height="575" + label="Ground Textures" layout="topleft" - left="10" - name="region_text_lbl" - top="10" - width="100"> + name="Textures" + width="258"> + <text + type="string" + length="1" + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left="10" + name="region_text_lbl" + top="10" + width="100"> Region: - </text> - <text - type="string" - length="1" - follows="left|top" - font="SansSerif" - height="20" - layout="topleft" - left_delta="50" - name="region_text" - top_delta="0" - width="200"> + </text> + <text + type="string" + length="1" + follows="left|top" + font="SansSerif" + height="20" + layout="topleft" + left_delta="50" + name="region_text" + top_delta="0" + width="200"> unknown - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="10" - name="detail_texture_text" - top="36" - width="300"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="detail_texture_text" + top="36" + width="300"> Terrain Textures -(requires 512x512, 24 bit .tga files) - </text> - <texture_picker - follows="left|top" - height="100" - layout="topleft" - left_delta="0" - name="texture_detail_0" - top_delta="30" - width="100" /> - <texture_picker - follows="left|top" - height="100" - layout="topleft" - left_pad="10" - name="texture_detail_1" - top_delta="0" - width="100" /> - <texture_picker - follows="left|top" - height="100" - layout="topleft" - left="10" - name="texture_detail_2" - top_delta="105" - width="100" /> - <texture_picker - follows="left|top" - height="100" - layout="topleft" - left_pad="10" - name="texture_detail_3" - top_delta="0" - width="100" /> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="10" - name="height_text_lbl" - top="157" - width="65"> + (requires 512x512, 24 bit .tga files) + </text> + <texture_picker + follows="left|top" + height="100" + layout="topleft" + left_delta="0" + name="texture_detail_0" + top_delta="30" + width="100" /> + <texture_picker + follows="left|top" + height="100" + layout="topleft" + left_pad="10" + name="texture_detail_1" + top_delta="0" + width="100" /> + <texture_picker + follows="left|top" + height="100" + layout="topleft" + left="10" + name="texture_detail_2" + top_delta="105" + width="100" /> + <texture_picker + follows="left|top" + height="100" + layout="topleft" + left_pad="10" + name="texture_detail_3" + top_delta="0" + width="100" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl" + top="157" + width="65"> 1 (Low) - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left_pad="45" - name="height_text_lbl2" - top_delta="0" - width="100"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_pad="45" + name="height_text_lbl2" + top_delta="0" + width="100"> 2 - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="10" - name="height_text_lbl3" - top_delta="105" - width="100"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl3" + top_delta="105" + width="100"> 3 - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left_pad="10" - name="height_text_lbl4" - top_delta="0" - width="100"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_pad="10" + name="height_text_lbl4" + top_delta="0" + width="100"> 4 (High) - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="10" - name="height_text_lbl5" - top_delta="25" - width="300"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl5" + top_delta="25" + width="300"> Texture Elevation Ranges - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="51" - name="height_text_lbl6" - top_delta="20" - width="100"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="51" + name="height_text_lbl6" + top_delta="20" + width="100"> Southwest - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left_pad="10" - name="height_text_lbl7" - top_delta="0" - width="100"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_pad="10" + name="height_text_lbl7" + top_delta="0" + width="100"> Northwest - </text> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="Low" - label_width="37" - layout="topleft" - left="10" - max_val="500" - min_val="-500" - name="height_start_spin_0" - top_delta="20" - width="100" /> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="Low" - label_width="37" - layout="topleft" - left_pad="10" - max_val="500" - min_val="-500" - name="height_start_spin_1" - top_delta="0" - width="100" /> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="High" - label_width="37" - layout="topleft" - left="10" - max_val="500" - min_val="-500" - name="height_range_spin_0" - top_delta="20" - width="100" /> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="High" - label_width="37" - layout="topleft" - left_pad="10" - max_val="500" - min_val="-500" - name="height_range_spin_1" - top_delta="0" - width="100" /> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="51" - name="height_text_lbl8" - top_delta="30" - width="100"> + </text> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left="10" + max_val="500" + min_val="-500" + name="height_start_spin_0" + top_delta="20" + width="100" /> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_start_spin_1" + top_delta="0" + width="100" /> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left="10" + max_val="500" + min_val="-500" + name="height_range_spin_0" + top_delta="20" + width="100" /> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_range_spin_1" + top_delta="0" + width="100" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="51" + name="height_text_lbl8" + top_delta="30" + width="100"> Southeast - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left_pad="10" - name="height_text_lbl9" - top_delta="0" - width="100"> + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_pad="10" + name="height_text_lbl9" + top_delta="0" + width="100"> Northeast - </text> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="Low" - label_width="37" - layout="topleft" - left="10" - max_val="500" - min_val="-500" - name="height_start_spin_2" - top_delta="20" - width="100" /> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="Low" - label_width="37" - layout="topleft" - left_pad="10" - max_val="500" - min_val="-500" - name="height_start_spin_3" - top_delta="0" - width="100" /> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="High" - label_width="37" - layout="topleft" - left="10" - max_val="500" - min_val="-500" - name="height_range_spin_2" - top_delta="20" - width="100" /> - <spinner - follows="left|top" - height="20" - increment="0.5" - label="High" - label_width="37" - layout="topleft" - left_pad="10" - max_val="500" - min_val="-500" - name="height_range_spin_3" - top_delta="0" - width="100" /> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left="10" - name="height_text_lbl10" - top_delta="30" - width="270"> + </text> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left="10" + max_val="500" + min_val="-500" + name="height_start_spin_2" + top_delta="20" + width="100" /> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="Low" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_start_spin_3" + top_delta="0" + width="100" /> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left="10" + max_val="500" + min_val="-500" + name="height_range_spin_2" + top_delta="20" + width="100" /> + <spinner + follows="left|top" + height="20" + increment="0.5" + label="High" + label_width="37" + layout="topleft" + left_pad="10" + max_val="500" + min_val="-500" + name="height_range_spin_3" + top_delta="0" + width="100" /> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left="10" + name="height_text_lbl10" + top_delta="30" + width="270"> These values represent the blend range -for the textures above. - </text> - <text - type="string" - length="1" - follows="left|top" - height="20" - layout="topleft" - left_delta="0" - name="height_text_lbl11" - top_delta="32" - width="270"> - Measured in meters, the LOW value -is the MAXIMUM height of Texture #1, -and the HIGH value is the MINIMUM -height of Texture #4. - </text> - - <button - enabled="false" - follows="left|bottom" - height="20" - label="Apply" - layout="topleft" - left="120" - name="apply_btn" - top_delta="60" - width="100" /> + for the textures above. + </text> + <text + type="string" + length="1" + follows="left|top" + height="20" + layout="topleft" + left_delta="0" + name="height_text_lbl11" + top_delta="32" + width="270"> + Measured in meters, the LOW value + is the MAXIMUM height of Texture #1, + and the HIGH value is the MINIMUM + height of Texture #4. + </text> + + <button + enabled="false" + follows="left|bottom" + height="20" + label="Apply" + layout="topleft" + left="120" + name="apply_btn" + top_delta="60" + width="100" /> + </panel> + </scroll_container> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_script_ed.xml b/indra/newview/skins/default/xui/en/panel_script_ed.xml index 9fd075f79b..5738879c5a 100644 --- a/indra/newview/skins/default/xui/en/panel_script_ed.xml +++ b/indra/newview/skins/default/xui/en/panel_script_ed.xml @@ -44,6 +44,8 @@ max_length="65536" name="Script Editor" width="492" + show_line_numbers="true" + handle_edit_keys_directly="true" word_wrap="true"> Loading... </text_editor> diff --git a/indra/newview/skins/default/xui/en/panel_side_tray.xml b/indra/newview/skins/default/xui/en/panel_side_tray.xml index 58be74a598..e9eac2c9dc 100644 --- a/indra/newview/skins/default/xui/en/panel_side_tray.xml +++ b/indra/newview/skins/default/xui/en/panel_side_tray.xml @@ -1,54 +1,54 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<side_tray - name="sidebar" - background_visible="false" - bg_opaque_color="0.0 0.0 0.0 0.0" - mouse_opaque="true" - width="305" - collapsed="true" +<side_tray + name="sidebar" + background_visible="false" + mouse_opaque="true" + width="305" + collapsed="true" > - <sidetray_tab + <sidetray_tab name="sidebar_home" tab_title="Home" description="Home." image="icn_voice-groupfocus.tga" - mouse_opaque="false" - background_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" + mouse_opaque="false" + background_opaque="false" + background_visible="true" > - <panel - name="panel_home" - filename="panel_sidetray_home_tab.xml" - label="home" - border="true" + <panel + name="panel_home" + filename="panel_sidetray_home_tab.xml" + label="home" + border="true" + font="SansSerifBold" /> </sidetray_tab> - <sidetray_tab + <sidetray_tab name="sidebar_people" tab_title="People" description="Find your friends, contacts and people nearby." image="TabIcon_People_Off" - mouse_opaque="false" - background_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" + mouse_opaque="false" + background_opaque="false" + background_visible="true" > <panel_container name="panel_container" > - <panel - class="panel_people" - name="panel_people" - filename="panel_people.xml" - border="true" + <panel + class="panel_people" + name="panel_people" + filename="panel_people.xml" + border="true" + font="SansSerifBold" /> - <panel - class="panel_profile_view" - name="panel_profile_view" + <panel + class="panel_profile_view" + name="panel_profile_view" filename="panel_profile_view.xml" border="true" + font="SansSerifBold" /> <panel class="panel_group_info_sidetray" @@ -60,532 +60,41 @@ </panel_container> </sidetray_tab> - <sidetray_tab + <sidetray_tab name="sidebar_places" tab_title="Places" label="Places" description="Find places to go and places you've been." - image="TabIcon_Places_Off" - mouse_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" + image="TabIcon_Places_Off" + mouse_opaque="false" + background_visible="true" > - <panel - class="panel_places" - name="panel_places" - filename="panel_places.xml" + <panel + class="panel_places" + name="panel_places" + filename="panel_places.xml" label="Places" border="true" /> </sidetray_tab> - - <sidetray_tab + + <sidetray_tab name="sidebar_me" - tab_title="Me" + tab_title="My Profile" description="Change your profile, your look and quick links to your outfits." image="TabIcon_Me_Off" - mouse_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" + mouse_opaque="false" + background_visible="true" > - <panel - class="panel_me_profile_view" - name="panel_me_profile" + <panel + class="panel_me_profile_view" + name="panel_me_profile" filename="panel_me_profile.xml" - label="Me" + label="My Profile" border="true" + font="SansSerifBold" /> </sidetray_tab> - <!-- - - <sidetray_tab - name="sidebar_previews" - mouse_opaque="false" - background_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" - image="icn_voice-groupfocus.tga" - tab_title="Previews" - description="Previews." - > - <accordion_tab - name="floater_preview_animation" - title="Preview Animation" - collapsible="true" - expanded="false" - min_width="200" - min_height="200" - header_visible="true" - > - <panel - class="floater_preview_animation" - name="floater_preview_animation" - filename="floater_preview_animation.xml" - width="280" - height="85" - label="Preview_Animation" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_preview_gesture" - title="Preview Gesture" - collapsible="true" - expanded="false" - min_width="200" - min_height="200" - header_visible="true" - > - <panel - class="floater_preview_gesture" - name="floater_preview_gesture" - filename="floater_preview_gesture.xml" - width="280" - height="400" - label="Preview_Gesture" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_preview_existing_landmark" - title="Preview Existing Landmark" - collapsible="true" - expanded="false" - min_width="200" - min_height="200" - header_visible="true" - > - <panel - class="floater_preview_existing_landmark" - name="floater_preview_existing_landmark" - filename="floater_preview_existing_landmark.xml" - width="280" - height="495" - label="Preview_Existing_Landmark" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_preview_sound" - title="Preview Sound" - collapsible="true" - expanded="false" - min_width="200" - min_height="200" - header_visible="true" - > - <panel - class="floater_preview_sound" - name="floater_preview_sound" - filename="floater_preview_sound.xml" - width="280" - height="85" - label="Preview_Sound" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_preview_url" - title="Preview URL" - collapsible="true" - expanded="false" - min_width="200" - min_height="200" - header_visible="true" - > - <panel - class="floater_preview_url" - name="floater_preview_url" - filename="floater_preview_url.xml" - width="280" - height="465" - label="Preview_URL" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_URL_entry" - title="URL Entry" - collapsible="true" - expanded="false" - min_width="200" - min_height="200" - header_visible="true" - > - <panel - class="floater_URL_entry" - name="floater_URL_entry" - filename="floater_URL_entry.xml" - width="280" - height="87" - label="URL_entry" - border="true" - /> - </accordion_tab> - </sidetray_tab> - - <sidetray_tab - name="sidebar_region" - mouse_opaque="false" - background_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" - image="icn_voice-groupfocus.tga" - tab_title="Region" - description="Region." - > - <accordion_tab - name="panel_region_covenant" - title="Region Covenant" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="panel_region_covenant" - name="panel_region_covenant" - filename="panel_region_covenant.xml" - width="280" - height="350" - label="Panel_Region_Covenant" - border="true" - /> - </accordion_tab> - <accordion_tab - name="panel_region_debug" - title="Region Debug" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="panel_region_debug" - name="panel_region_debug" - filename="panel_region_debug.xml" - width="280" - height="500" - label="Panel_Region_Debug" - border="true" - /> - </accordion_tab> - <accordion_tab - name="panel_region_estate" - title="Region Estate" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="panel_region_estate" - name="panel_region_estate" - filename="panel_region_estate.xml" - width="280" - height="950" - label="Panel_Region_Estate" - border="true" - /> - </accordion_tab> - <accordion_tab - name="panel_region_general" - title="Region General" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="panel_region_general" - name="panel_region_general" - filename="panel_region_general.xml" - width="280" - height="500" - label="Panel_Region_General" - border="true" - /> - </accordion_tab> - <accordion_tab - name="panel_region_terrain" - title="Region Terrain" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="panel_region_terrain" - name="panel_region_terrain" - filename="panel_region_terrain.xml" - width="280" - height="340" - label="Panel_Region_Terrain" - border="true" - /> - </accordion_tab> - <accordion_tab - name="panel_region_texture" - title="Region Texture" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="panel_region_texture" - name="panel_region_texture" - filename="panel_region_texture.xml" - width="280" - height="575" - label="Panel_Region_Texture" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_region_info" - title="Region Info" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_region_info" - name="floater_region_info" - filename="floater_region_info.xml" - width="280" - height="512" - label="Floater_Region_Info" - border="true" - /> - </accordion_tab> - </sidetray_tab> - - <sidetray_tab - name="Build" - mouse_opaque="false" - background_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" - image="icn_voice-groupfocus.tga" - tab_title="Build" - description="Build" - > - <accordion_tab - name="floater_tools" - title="Tools" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_tools" - name="floater_tools" - filename="floater_tools.xml" - width="280" - height="550" - label="Tools" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_bulk_perms" - title="Bulk Perms" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_bulk_perms" - name="floater_bulk_perms" - filename="floater_bulk_perms.xml" - width="280" - height="360" - label="Tools" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_build_options" - title="Build Options" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_build_options" - name="floater_build_options" - filename="floater_build_options.xml" - width="280" - height="151" - label="Tools" - border="true" - /> - </accordion_tab> - - </sidetray_tab> - - <sidetray_tab - name="othertools" - mouse_opaque="false" - background_opaque="false" - background_visible="true" - bg_opaque_color="0.5 0.5 0.5 1.0" - image="icn_voice-groupfocus.tga" - tab_title="Other Tools" - description="Other Tools" - > - <accordion_tab - name="floater_gesture" - title="Gestures" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_gesture" - name="floater_gesture" - filename="floater_gesture.xml" - width="280" - height="465" - label="Gesture" - border="true" - /> - </accordion_tab> - - <accordion_tab - name="floater_buy_contents" - title="Buy Contents" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_buy_contents" - name="floater_buy_contents" - filename="floater_buy_contents.xml" - width="280" - height="250" - label="buy_contents" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_buy_object" - title="Buy Object" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_buy_object" - name="floater_buy_object" - filename="floater_buy_object.xml" - width="280" - height="250" - label="buy_object" - border="true" - /> - </collapsible_ctrl> - <accordion_tab - name="floater_inventory_view_finder" - title="Inventory View Finder" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_inventory_view_finder" - name="floater_inventory_view_finder" - filename="floater_inventory_view_finder.xml" - width="280" - height="408" - label="view_finder" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_mute" - title="Mute" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_mute" - name="floater_mute" - filename="floater_mute.xml" - width="280" - height="300" - label="mute" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_sell_land" - title="Sell Land" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_sell_land" - name="floater_sell_land" - filename="floater_sell_land.xml" - width="280" - height="600" - label="sell_land" - border="true" - /> - </accordion_tab> - <accordion_tab - name="floater_telehub" - title="Telehub" - collapsible="true" - min_width="200" - min_height="200" - expanded="false" - header_visible="true" - > - <panel - class="floater_telehub" - name="floater_telehub" - filename="floater_telehub.xml" - width="280" - height="250" - label="telehub" - border="true" - /> - </accordion_tab> - - </sidetray_tab> - --> <sidetray_tab name="sidebar_appearance" @@ -607,4 +116,3 @@ </side_tray> - diff --git a/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml b/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml index 4ae90380f8..a5c3be3349 100644 --- a/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml +++ b/indra/newview/skins/default/xui/en/panel_side_tray_tab_caption.xml @@ -1,15 +1,21 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<panel name="sidetray_tab_panel" - bottom="0" height="25" left="0" background_visible="false" - bg_visible="false" border="false" border_visible="false" - bg_opaque_color="0.7 0.3 0.7 1.0" - follows="left|top|right" - mouse_opaque="true"> - <text type="string" length="1" bg_visible="false" border_visible="false" - bottom="0" enabled="true" follows="left|top" - height="20" left="10" - font="SansSerifBold" halign="left" - name="sidetray_tab_title" width="100"> - Side Panel - </text> +<panel + background_visible="true" + bottom="0" + follows="left|top|right" + height="30" + layout="topleft" + left="0" + name="sidetray_tab_panel"> + <text + follows="left|top" + font="SansSerifHuge" + height="16" + layout="topleft" + left="10" + name="sidetray_tab_title" + text_color="white" + top="4" + value="Side Panel" + width="255" /> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml b/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml index f10161cecd..e87517e200 100644 --- a/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml +++ b/indra/newview/skins/default/xui/en/panel_sidetray_home_tab.xml @@ -1,115 +1,203 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes"?> -<!-- All our XML is utf-8 encoded. --> - +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel - name="home_tab" - title="home_tab" - visible="true" - width="300" - height="420" - background_opaque="false" - background_visible="true" - bevel_style="in" - follows="left|top|right|bottom"> - <panel - left="10" width="280" height="130" - background_visible="true" - background_opaque="false" - bg_alpha_color="0.3 0.3 0.3 1.0" - name="sidebar_people" - follows="left|top|right" - class="panel_sidetray_home_info"> - <text - top="-10" width="200" left="5" height="30" follows="left|right|top" - font="SansSerifHugeBold" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_name" > - People - </text> - <icon - top="-10" right="-10" width="20" height="20" follows="top|right" - color="1 1 1 1" enabled="true" image_name="icn_voice-groupfocus.tga" - mouse_opaque="false" name="tab_icon"/> - <text - top="-40" left="10" right="-10" height="120" follows="left|right|bottom" - font="SansSerifBig" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_description" > - Find your friends, contacts and people nearby. - </text> - </panel> - <panel - left="10" width="280" height="130" - background_visible="true" - background_opaque="false" - bg_alpha_color="0.3 0.3 0.3 1.0" - name="sidebar_places" - follows="left|top|right" - class="panel_sidetray_home_info"> - <text - top="-10" width="200" left="5" height="30" follows="left|right|top" - font="SansSerifHugeBold" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_name" > - Places - </text> - <icon - top="-10" right="-10" width="20" height="20" follows="top|right" - color="1 1 1 1" enabled="true" image_name="inv_item_landmark.tga" - mouse_opaque="false" name="tab_icon"/> - <text - top="-40" left="10" right="-10" height="120" follows="left|right|bottom|top" - font="SansSerifBig" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_description" > - Find your friends, contacts and people nearby. - </text> - </panel> - <panel - left="10" width="280" height="130" - background_visible="true" - background_opaque="false" - bg_alpha_color="0.3 0.3 0.3 1.0" - name="sidebar_me" - follows="left|top|right" - class="panel_sidetray_home_info"> - <text - top="-10" width="200" left="5" height="30" follows="left|right|top" - font="SansSerifHugeBold" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_name" > - Me - </text> - <icon - top="-10" right="-10" width="20" height="20" follows="top|right" - color="1 1 1 1" enabled="true" image_name="icn_voice-pvtfocus.tga" - mouse_opaque="false" name="tab_icon"/> - <text - top="-40" left="10" right="-10" height="120" follows="left|right|bottom" - font="SansSerifBig" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_description" > - Change your profile, your look and quick links to your outfits. - </text> - </panel> - <panel - left="10" width="280" height="130" - background_visible="true" - background_opaque="false" - bg_alpha_color="0.3 0.3 0.3 1.0" - name="sidebar_appearance" - follows="left|top|right" - class="panel_sidetray_home_info"> - <text - top="-10" width="200" left="5" height="30" follows="left|right|top" - font="SansSerifHugeBold" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_name" > - Appearance - </text> - <icon - top="-10" right="-10" width="20" height="20" follows="top|right" - color="1 1 1 1" enabled="true" image_name="inv_item_shirt.tga" - mouse_opaque="false" name="tab_icon"/> - <text - top="-40" left="10" right="-10" height="120" follows="left|right|bottom" - font="SansSerifBig" text_color="white" word_wrap="true" - mouse_opaque="false" name="tab_description" > - Change your appearance. - </text> - </panel> -</panel> -
\ No newline at end of file + background_visible="true" + bevel_style="in" + follows="left|top|right|bottom" + height="420" + label="home_tab" + layout="topleft" + name="home_tab" + width="300"> + <panel + background_visible="true" + bg_alpha_color="DkGray2" + class="panel_sidetray_home_info" + follows="left|top|right" + height="130" + layout="topleft" + left="10" + name="sidebar_people" + width="280"> + <text + follows="left|right|top" + font="SansSerifBigBold" + height="30" + layout="topleft" + left="5" + mouse_opaque="false" + name="tab_name" + text_color="white" + top="10" + value="People" + width="200" + word_wrap="true" /> + <icon + color="DkGray" + follows="top|right" + height="20" + layout="topleft" + name="tab_icon" + right="-10" + top="10" + image_name="TabIcon_Home_Selected" + width="20" /> + <text + follows="left|right|bottom" + font="SansSerifBold" + height="120" + layout="topleft" + left="10" + mouse_opaque="false" + name="tab_description" + right="-10" + text_color="white" + top="40" + word_wrap="true"> + Find your friends, contacts and people nearby. + </text> + </panel> + <panel + background_visible="true" + bg_alpha_color="DkGray2" + class="panel_sidetray_home_info" + follows="left|top|right" + height="130" + layout="topleft" + left="10" + name="sidebar_places" + width="280"> + <text + follows="left|right|top" + font="SansSerifBigBold" + height="30" + layout="topleft" + left="5" + mouse_opaque="false" + name="tab_name" + text_color="white" + top="10" + value="Places" + width="200" + word_wrap="true" /> + <icon + color="DkGray" + follows="top|right" + height="20" + layout="topleft" + name="tab_icon" + right="-10" + top="10" + width="20" + image_name="TabIcon_Places_Selected"/> + <text + follows="left|right|bottom|top" + font="SansSerifBold" + height="120" + layout="topleft" + left="10" + mouse_opaque="false" + name="tab_description" + right="-10" + text_color="white" + top="40" + word_wrap="true"> + Find your friends, contacts and people nearby. + </text> + </panel> + <panel + background_visible="true" + bg_alpha_color="DkGray2" + class="panel_sidetray_home_info" + follows="left|top|right" + height="130" + layout="topleft" + left="10" + name="sidebar_me" + width="280"> + <text + follows="left|right|top" + font="SansSerifBigBold" + height="30" + layout="topleft" + left="5" + mouse_opaque="false" + name="tab_name" + text_color="white" + top="10" + value="My Profile" + width="200" + word_wrap="true" /> + <icon + color="DkGray" + follows="top|right" + height="20" + layout="topleft" + name="tab_icon" + right="-10" + top="10" + width="20" + image_name="TabIcon_Me_Selected"/> + <text + follows="left|right|bottom|top" + font="SansSerifBold" + height="120" + layout="topleft" + left="10" + mouse_opaque="false" + name="tab_description" + right="-10" + text_color="white" + top="40" + word_wrap="true"> + Change your profile. + </text> + </panel> + <panel + background_visible="true" + bg_alpha_color="DkGray2" + class="panel_sidetray_home_info" + follows="left|top|right" + height="130" + layout="topleft" + left="10" + name="sidebar_appearance" + width="280"> + <text + follows="left|right|top" + font="SansSerifBigBold" + height="30" + layout="topleft" + left="5" + mouse_opaque="false" + name="tab_name" + text_color="white" + top="10" + value="My Appearance" + width="200" + word_wrap="true" /> + <icon + color="DkGray" + follows="top|right" + height="20" + layout="topleft" + name="tab_icon" + right="-10" + top="10" + width="20" + image_name="TabIcon_Things_Selected"/> + <text + follows="left|right|bottom|top" + font="SansSerifBold" + height="120" + layout="topleft" + left="10" + mouse_opaque="false" + name="tab_description" + right="-10" + text_color="white" + top="40" + word_wrap="true"> + Change your apperance and looks. + </text> + </panel> +</panel> diff --git a/indra/newview/skins/default/xui/en/panel_stand_stop_flying.xml b/indra/newview/skins/default/xui/en/panel_stand_stop_flying.xml index 445c9cc288..4d91a544c3 100644 --- a/indra/newview/skins/default/xui/en/panel_stand_stop_flying.xml +++ b/indra/newview/skins/default/xui/en/panel_stand_stop_flying.xml @@ -15,6 +15,7 @@ name="stand_btn" tool_tip="Click here to stand up." top="2" + visible="false" width="115" /> <button follows="left|bottom" @@ -24,5 +25,6 @@ name="stop_fly_btn" tool_tip="Stop Flying" top="2" + visible="false" width="115" /> </panel> diff --git a/indra/newview/skins/default/xui/en/panel_status_bar.xml b/indra/newview/skins/default/xui/en/panel_status_bar.xml index 82b7e02aeb..f597211ec9 100644 --- a/indra/newview/skins/default/xui/en/panel_status_bar.xml +++ b/indra/newview/skins/default/xui/en/panel_status_bar.xml @@ -197,52 +197,6 @@ top_delta="-3" visible="false" width="16" /> - <button - follows="right|bottom" - height="16" - image_disabled="sm_rounded_corners_simple.tga" - image_disabled_selected="sm_rounded_corners_simple.tga" - image_hover_selected="sm_rounded_corners_simple.tga" - image_hover_unselected="sm_rounded_corners_simple.tga" - image_selected="sm_rounded_corners_simple.tga" - image_unselected="sm_rounded_corners_simple.tga" - layout="topleft" - left_pad="313" - mouse_opaque="false" - name="menubar_search_bevel_bg" - picture_style="true" - top_delta="0" - width="94" /> - <line_editor - bevel_style="none" - border_style="line" - commit_on_focus_lost="false" - follows="right|bottom" - height="11" - label="Search" - layout="topleft" - left_delta="1" - name="search_editor" - tab_group="1" - tool_tip="Search Second Life" - top_delta="4" - width="78" /> - <button - follows="right|bottom" - font="SansSerifSmall" - height="16" - image_disabled="status_search_btn.png" - image_disabled_selected="status_search_btn_pressed.png" - image_selected="status_search_btn_pressed.png" - image_unselected="status_search_btn.png" - layout="topleft" - left_delta="78" - name="search_btn" - picture_style="true" - scale_image="false" - tool_tip="Search Second Life" - top_delta="-4" - width="16" /> <text enabled="false" follows="right|bottom" diff --git a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml index eccb919b04..3ac15fe550 100644 --- a/indra/newview/skins/default/xui/en/panel_sys_well_item.xml +++ b/indra/newview/skins/default/xui/en/panel_sys_well_item.xml @@ -10,7 +10,7 @@ width="318" height="35" layout="topleft" - follows="top" + follows="left|right" background_opaque="false" background_visible="true" bg_alpha_color="0.0 0.0 0.0 0.0" > @@ -54,6 +54,7 @@ name="close_btn" mouse_opaque="true" label="" + tab_stop="false" image_unselected="toast_hide_btn.tga" image_disabled="toast_hide_btn.tga" image_selected="toast_hide_btn.tga" diff --git a/indra/newview/skins/default/xui/en/panel_teleport_history.xml b/indra/newview/skins/default/xui/en/panel_teleport_history.xml index 05ebcbb50b..82d6945e0f 100644 --- a/indra/newview/skins/default/xui/en/panel_teleport_history.xml +++ b/indra/newview/skins/default/xui/en/panel_teleport_history.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <panel name="Teleport History" bottom="0" height="326" left="0" width="380" border="true" follows="left|top|right|bottom"> - <scroll_list bottom="0" column_padding="0" draw_heading="true" + <scroll_list bottom="0" column_padding="0" draw_heading="false" draw_stripes="false" follows="left|top|bottom|right" left="0" multi_select="false" name="history_items" search_column="1" sort_column="1" height="326" width="380" > diff --git a/indra/newview/skins/default/xui/en/panel_toast.xml b/indra/newview/skins/default/xui/en/panel_toast.xml index f7d8cf948e..441caffa28 100644 --- a/indra/newview/skins/default/xui/en/panel_toast.xml +++ b/indra/newview/skins/default/xui/en/panel_toast.xml @@ -62,6 +62,7 @@ mouse_opaque="true" name="hide_btn" label="" + tab_stop="false" image_unselected="toast_hide_btn.tga" image_disabled="toast_hide_btn.tga" image_selected="toast_hide_btn.tga" diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml index 0ed473a01a..df328118a2 100644 --- a/indra/newview/skins/default/xui/en/strings.xml +++ b/indra/newview/skins/default/xui/en/strings.xml @@ -7,9 +7,9 @@ <!-- Default Args - these arguments will be replaced in all strings --> <string name="SECOND_LIFE">Second Life</string> - <string name="SECOND_LIFE_VIEWER">Second Life</string> - <string name="SECOND_LIFE_GRID">Second Life</string> - <string name="SECOND_LIFE_SUPPORT">Second Life Support Portal</string> + <string name="APP_NAME">Second Life</string> + <string name="SECOND_LIFE_GRID">Second Life Grid</string> + <string name="SUPPORT_SITE">Second Life Support Portal</string> <!-- starting up --> <string name="StartupDetectingHardware">Detecting hardware...</string> @@ -19,7 +19,7 @@ <string name="CacheNobody">(nobody)</string> <string name="CacheNone">(none)</string> <!-- Login --> - <string name="LoginInProgress">Logging in. [SECOND_LIFE_VIEWER] may appear frozen. Please wait.</string> + <string name="LoginInProgress">Logging in. [APP_NAME] may appear frozen. Please wait.</string> <string name="LoginInProgressNoFrozen">Logging in...</string> <string name="LoginAuthenticating">Authenticating</string> <string name="LoginMaintenance">Performing account maintenance...</string> @@ -149,6 +149,7 @@ <string name="gesture">gesture</string> <string name="simstate">simstate</string> <string name="favorite">favorite</string> + <string name="symbolic link">link</string> <!-- llvoavatar. Displayed in the avatar chat bubble --> <string name="AvatarEditingAppearance">(Editing Appearance)</string> @@ -610,6 +611,8 @@ you managed for [OWNER] Unknown file extension .%s Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh </string> + <string name="AddLandmarkNavBarMenu">Add Landmark...</string> + <string name="EditLandmarkNavBarMenu">Edit Landmark...</string> <!-- Previews --> <string name="FileSaved">File Saved</string> @@ -663,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> @@ -680,29 +683,29 @@ Expected .wav, .tga, .bmp, .jpg, .jpeg, or .bvh Please see: http://wiki.secondlife.com/wiki/Client_parameters Error: </string> - <string name="MBCmdLineUsg">[SECOND_LIFE] Command line usage:</string> + <string name="MBCmdLineUsg">[APP_NAME] Command line usage:</string> <string name="MBUnableToAccessFile"> - [SECOND_LIFE] is unable to access a file that it needs. + [APP_NAME] is unable to access a file that it needs. This can be because you somehow have multiple copies running, or your system incorrectly thinks a file is open. If this message persists, restart your computer and try again. -If it continues to persist, you may need to completely uninstall [SECOND_LIFE] and reinstall it. +If it continues to persist, you may need to completely uninstall [APP_NAME] and reinstall it. </string> <string name="MBFatalError">Fatal Error</string> - <string name="MBRequiresAltiVec"> [SECOND_LIFE] requires a processor with AltiVec (G4 or later).</string> + <string name="MBRequiresAltiVec"> [APP_NAME] requires a processor with AltiVec (G4 or later).</string> <string name="MBAlreadyRunning"> - [SECOND_LIFE] is already running. + [APP_NAME] is already running. Check your task bar for a minimized copy of the program. If this message persists, restart your computer. </string> <string name="MBFrozenCrashed"> - [SECOND_LIFE] appears to have frozen or crashed on the previous run. + [APP_NAME] appears to have frozen or crashed on the previous run. Would you like to send a crash report? </string> <string name="MBAlert">Alert</string> <string name="MBNoDirectX"> - [SECOND_LIFE] is unable to detect DirectX 9.0b or greater. -[SECOND_LIFE] uses DirectX to detect hardware and/or outdated drivers that can cause stability problems, poor performance and crashes. While you can run [SECOND_LIFE] without it, we highly recommend running with DirectX 9.0b. + [APP_NAME] is unable to detect DirectX 9.0b or greater. +[APP_NAME] uses DirectX to detect hardware and/or outdated drivers that can cause stability problems, poor performance and crashes. While you can run [APP_NAME] without it, we highly recommend running with DirectX 9.0b. Do you wish to continue? </string> @@ -723,23 +726,23 @@ Running in window. <string name="MBPixelFmtErr">Can't find suitable pixel format</string> <string name="MBPixelFmtDescErr">Can't get pixel format description</string> <string name="MBTrueColorWindow"> - [SECOND_LIFE] requires True Color (32-bit) to run in a window. + [APP_NAME] requires True Color (32-bit) to run in a window. Please go to Control Panels > Display > Settings and set the screen to 32-bit color. -Alternately, if you choose to run fullscreen, [SECOND_LIFE] will automatically adjust the screen each time it runs. +Alternately, if you choose to run fullscreen, [APP_NAME] will automatically adjust the screen each time it runs. </string> <string name="MBAlpha"> - [SECOND_LIFE] is unable to run because it can't get an 8 bit alpha channel. Usually this is due to video card driver issues. + [APP_NAME] is unable to run because it can't get an 8 bit alpha channel. Usually this is due to video card driver issues. Please make sure you have the latest video card drivers installed. Also be sure your monitor is set to True Color (32-bit) in Control Panels > Display > Settings. -If you continue to receive this message, contact customer support. +If you continue to receive this message, contact the [SUPPORT_SITE]. </string> <string name="MBPixelFmtSetErr">Can't set pixel format</string> <string name="MBGLContextErr">Can't create GL rendering context</string> <string name="MBGLContextActErr">Can't activate GL rendering context</string> <string name="MBVideoDrvErr"> - [SECOND_LIFE] is unable to run because your video card drivers did not install properly, are out of date, or are for unsupported hardware. Please make sure you have the latest video card drivers and even if you do have the latest, try reinstalling them. + [APP_NAME] is unable to run because your video card drivers did not install properly, are out of date, or are for unsupported hardware. Please make sure you have the latest video card drivers and even if you do have the latest, try reinstalling them. -If you continue to receive this message, contact customer support. +If you continue to receive this message, contact the [SUPPORT_SITE]. </string> <!-- Avatar Shape Information --> @@ -933,4 +936,42 @@ 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> + + <!-- Favorites Bar --> + <string name="location_ctrl_add_landmark">Add to My Landmarks</string> + <string name="location_ctrl_edit_landmark">Edit My Landmark</string> + <string name="favorite_landmark_loading_tooltip">(Loading...)</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/skins/default/xui/en/teleport_strings.xml b/indra/newview/skins/default/xui/en/teleport_strings.xml index 616dc1a1d4..e8f6b1319a 100644 --- a/indra/newview/skins/default/xui/en/teleport_strings.xml +++ b/indra/newview/skins/default/xui/en/teleport_strings.xml @@ -2,21 +2,16 @@ <teleport_messages> <message_set name="errors"> <message name="invalid_tport"> - Problem encountered processing your teleport request. You may -need to log back in before you can teleport. If you continue -to get this message, please check the Tech Support FAQ at: -www.secondlife.com/support + Problem encountered processing your teleport request. You may need to log back in before you can teleport. +If you continue to get this message, please check the [SUPPORT_SITE]. </message> <message name="invalid_region_handoff"> - Problem encountered processing your region crossing. You may -need to log back in before you can cross regions. If you continue -to get this message, please check the Tech Support FAQ at: -www.secondlife.com/support. + Problem encountered processing your region crossing. You may need to log back in before you can cross regions. +If you continue to get this message, please check the [SUPPORT_SITE]. </message> <message name="blocked_tport"> Sorry, teleport is currently blocked. Try again in a moment. -If you still cannot teleport, please log out and log back in to -resolve the problem. +If you still cannot teleport, please log out and log back in to resolve the problem. </message> <message name="nolandmark_tport"> Sorry, but system was unable to locate landmark destination. @@ -29,27 +24,19 @@ Try again in a moment. Sorry, you do not have access to that teleport destination. </message> <message name="missing_attach_tport"> - Your attachments have not arrived yet. Try waiting for a few -more seconds or log out and back in again before attempting -to teleport. + Your attachments have not arrived yet. Try waiting for a few more seconds or log out and back in again before attempting to teleport. </message> <message name="too_many_uploads_tport"> - The asset queue in this region is currently clogged so your teleport -request will not be able to succeed in a timely manner. Please try again -in a few minutes or go to a less busy area. + The asset queue in this region is currently clogged so your teleport request will not be able to succeed in a timely manner. Please try again in a few minutes or go to a less busy area. </message> <message name="expired_tport"> - Sorry, but the system was unable to complete your teleport request -in a timely fashion. Please try again in a few minutes. + Sorry, but the system was unable to complete your teleport request in a timely fashion. Please try again in a few minutes. </message> <message name="expired_region_handoff"> - Sorry, but the system was unable to complete your region crossing -in a timely fashion. Please try again in a few minutes. + Sorry, but the system was unable to complete your region crossing in a timely fashion. Please try again in a few minutes. </message> <message name="no_host"> - Unable to find teleport destination. The destination may be -temporarily unavailable or no longer exists. Please try again -in a few minutes. + Unable to find teleport destination. The destination may be temporarily unavailable or no longer exists. Please try again in a few minutes. </message> <message name="no_inventory_host"> The inventory system is currently unavailable. diff --git a/indra/newview/skins/default/xui/en/widgets/filter_editor.xml b/indra/newview/skins/default/xui/en/widgets/filter_editor.xml index aef3363259..d7736832a3 100644 --- a/indra/newview/skins/default/xui/en/widgets/filter_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/filter_editor.xml @@ -1,6 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<filter_editor select_on_focus="true"
- background="TextField_Search_Off"
background_image_disabled="TextField_Search_Disabled"
background_image_focused="TextField_Search_Active">
<clear_filter_button label=""
diff --git a/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml b/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml index 17e9a7beb2..26af76d8a3 100644 --- a/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml +++ b/indra/newview/skins/default/xui/en/widgets/gesture_combo_box.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <gesture_combo_box font="SansSerifSmall" + label="Gestures" list_position="below" max_chars="20" follows="right|top"> @@ -26,5 +27,4 @@ <gesture_combo_box.combo_editor name="Combo Text Entry" select_on_focus="true" font="SansSerifSmall" /> - <gesture_combo_box.item label="Gestures" /> </gesture_combo_box> diff --git a/indra/newview/skins/default/xui/en/widgets/location_input.xml b/indra/newview/skins/default/xui/en/widgets/location_input.xml index 777651c452..8f4d0edf95 100644 --- a/indra/newview/skins/default/xui/en/widgets/location_input.xml +++ b/indra/newview/skins/default/xui/en/widgets/location_input.xml @@ -9,7 +9,8 @@ <location_input font="SansSerifSmall" add_landmark_image_enabled="Favorite_Star_Active" add_landmark_image_disabled="Favorite_Star_Off" - hover_glow_amount="0.15" + add_landmark_image_hover="Favorite_Star_Over" + add_landmark_image_selected="Favorite_Star_Press" add_landmark_hpad="2" allow_text_entry="true" list_position="below" @@ -30,6 +31,9 @@ image_disabled="Info_Off" /> <add_landmark_button name="Add Landmark" label="" + hover_glow_amount="0.15" + image_hover_selected="Favorite_Star_Over" + image_hover_unselected="Favorite_Star_Over" width="16" height="16" tool_tip="Add to My Landmarks" diff --git a/indra/newview/skins/default/xui/en/widgets/scroll_bar.xml b/indra/newview/skins/default/xui/en/widgets/scroll_bar.xml index 4ad11c41da..48bc021e6d 100644 --- a/indra/newview/skins/default/xui/en/widgets/scroll_bar.xml +++ b/indra/newview/skins/default/xui/en/widgets/scroll_bar.xml @@ -9,17 +9,17 @@ <up_button image_unselected="ScrollArrow_Up" image_selected="ScrollArrow_Up" scale_image="true" thickness="15" - hover_glow_amount="0.15"/> + hover_glow_amount="0.35"/> <down_button image_unselected="ScrollArrow_Down" image_selected="ScrollArrow_Down" scale_image="true" thickness="15" - hover_glow_amount="0.15"/> + hover_glow_amount="0.35"/> <left_button image_unselected="ScrollArrow_Left" image_selected="ScrollArrow_Left" scale_image="true" thickness="15" - hover_glow_amount="0.15"/> + hover_glow_amount="0.35"/> <right_button image_unselected="ScrollArrow_Right" image_selected="ScrollArrow_Right" scale_image="true" thickness="15" - hover_glow_amount="0.15"/> + hover_glow_amount="0.35"/> </scroll_bar> diff --git a/indra/newview/skins/default/xui/en/widgets/scroll_container.xml b/indra/newview/skins/default/xui/en/widgets/scroll_container.xml index cb9ef04797..86356ff563 100644 --- a/indra/newview/skins/default/xui/en/widgets/scroll_container.xml +++ b/indra/newview/skins/default/xui/en/widgets/scroll_container.xml @@ -1,3 +1,5 @@ <?xml version="1.0" encoding="utf-8" standalone="yes" ?> <scroll_container color="black" - opaque="false"/> + opaque="false" + min_auto_scroll_rate="120" + max_auto_scroll_rate="500"/> diff --git a/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml b/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml index 960c4e81e5..4f2261c953 100644 --- a/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml +++ b/indra/newview/skins/default/xui/en/widgets/simple_text_editor.xml @@ -9,14 +9,14 @@ word_wrap="false" ignore_tab="true" track_bottom="false" - takes_non_scroll_clicks="true" cursor_color="TextCursorColor" default_color="TextDefaultColor" text_color="TextFgColor" text_readonly_color="TextFgReadOnlyColor" bg_readonly_color="TextBgReadOnlyColor" bg_writeable_color="TextBgWriteableColor" - bg_focus_color="TextBgFocusColor"> + bg_focus_color="TextBgFocusColor" + link_color="HTMLLinkColor"> <simple_text_editor.border bevel_style="in" follows="all" /> diff --git a/indra/newview/skins/default/xui/es/floater_joystick.xml b/indra/newview/skins/default/xui/es/floater_joystick.xml index 527485e57d..da7753a7d7 100644 --- a/indra/newview/skins/default/xui/es/floater_joystick.xml +++ b/indra/newview/skins/default/xui/es/floater_joystick.xml @@ -3,13 +3,14 @@ <check_box name="enable_joystick"> Activar el joystick: </check_box> - <spinner label="Mapping: X" name="JoystickAxis1"/> - <spinner label="Mapping: Y" name="JoystickAxis2"/> - <spinner label="Mapping: Z" name="JoystickAxis0"/> - <spinner label="Mapping: arriba/abajo" name="JoystickAxis4"/> - <spinner label="Mapping: izq./der." name="JoystickAxis5"/> - <spinner label="Mapping: giro" name="JoystickAxis3"/> - <spinner label="Mapping: zoom" name="JoystickAxis6"/> + <text left="140" name="joystick_type" width="360"/> + <spinner label="Mapping: eje X" name="JoystickAxis1" label_width="118" width="161"/> + <spinner label="Mapping: eje Y" name="JoystickAxis2" label_width="105" width="148"/> + <spinner label="Mapping: eje Z" name="JoystickAxis0" label_width="95" width="138"/> + <spinner label="Mapping: arriba/abajo" name="JoystickAxis4" label_width="118" width="161"/> + <spinner label="Mapping: izq./der." name="JoystickAxis5" label_width="105" width="148"/> + <spinner label="Mapping: giro" name="JoystickAxis3" label_width="95" width="138"/> + <spinner label="Mapping: zoom" name="JoystickAxis6" label_width="118" width="161"/> <check_box label="Zoom directo" name="ZoomDirect"/> <check_box label="Cursor 3D" name="Cursor3D"/> <check_box label="Nivel automático" name="AutoLeveling"/> @@ -19,7 +20,7 @@ <check_box name="JoystickAvatarEnabled"> Avatar </check_box> - <check_box name="JoystickBuildEnabled"> + <check_box name="JoystickBuildEnabled" left="192"> Construir </check_box> <check_box name="JoystickFlycamEnabled"> @@ -34,7 +35,7 @@ <text name="ZScale"> Escala: Z </text> - <text name="PitchScale"> + <text name="PitchScale" left="3" width="115"> Escala: arriba/abajo </text> <text name="YawScale"> @@ -52,11 +53,11 @@ <text name="ZDeadZone"> Zona muerta Z </text> - <text name="PitchDeadZone"> - Zona muerta arriba/abajo + <text name="PitchDeadZone" left="3" width="115"> + Zona muerta arri./aba. </text> - <text name="YawDeadZone"> - Zona muerta izq./der + <text name="YawDeadZone" left="3" width="115"> + Zona muerta izq./der. </text> <text name="RollDeadZone"> Zona muerta giro @@ -70,9 +71,9 @@ <text name="ZoomDeadZone"> Zona muerta zoom </text> - <button label="Predeterminados del SpaceNavigator" name="SpaceNavigatorDefaults"/> - <button label="OK" label_selected="OK" name="ok_btn"/> - <button label="Cancelar" label_selected="Cancelar" name="cancel_btn"/> + <button label="Predeterminados del SpaceNavigator" name="SpaceNavigatorDefaults" font="SansSerifSmall" left="330" width="210"/> + <button label="OK" label_selected="OK" name="ok_btn" left="330"/> + <button label="Cancelar" label_selected="Cancelar" name="cancel_btn" left_delta="120"/> <string name="JoystickMonitor"> Monitor del joystick </string> diff --git a/indra/newview/skins/default/xui/es/menu_inventory.xml b/indra/newview/skins/default/xui/es/menu_inventory.xml index 557123d4f8..28d47fce52 100644 --- a/indra/newview/skins/default/xui/es/menu_inventory.xml +++ b/indra/newview/skins/default/xui/es/menu_inventory.xml @@ -12,7 +12,7 @@ <menu_item_call label="Script nuevo" name="New Script"/> <menu_item_call label="Nota nueva" name="New Note"/> <menu_item_call label="Gesto nuevo" name="New Gesture"/> - <menu name="New Clothes"> + <menu name="New Clothes" label="Nueva ropa"> <menu_item_call label="Camisa nueva" name="New Shirt"/> <menu_item_call label="Pantalones nuevos" name="New Pants"/> <menu_item_call label="Zapatos nuevos" name="New Shoes"/> @@ -23,7 +23,7 @@ <menu_item_call label="Camiseta nueva" name="New Undershirt"/> <menu_item_call label="Ropa interior nueva" name="New Underpants"/> </menu> - <menu name="New Body Parts"> + <menu name="New Body Parts" label="Nuevas partes del cuerpo"> <menu_item_call label="Forma nueva" name="New Shape"/> <menu_item_call label="Piel nueva" name="New Skin"/> <menu_item_call label="Pelo nuevo" name="New Hair"/> diff --git a/indra/newview/skins/default/xui/es/notifications.xml b/indra/newview/skins/default/xui/es/notifications.xml index d7e480c653..f045c6fe9e 100644 --- a/indra/newview/skins/default/xui/es/notifications.xml +++ b/indra/newview/skins/default/xui/es/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> Elegir siempre esta opción </global> + <global name="implicitclosebutton"> + Cerrar + </global> <template name="okbutton"> <form> <button @@ -189,7 +192,7 @@ No podrá removérseles de ese rol, sino que deberán renunciar a él por sí mi <usetemplate name="okcancelbuttons" notext="No" yestext="Sí"/> </notification> <notification name="ClickPublishHelpLand"> - Seleccionar "Publicar en la web" + Seleccionar 'Publicar en la web' Marcando este ítem, se mostrará: - esta parcela en los resultados de la búsqueda - los objetos públicos de esta parcela @@ -219,7 +222,7 @@ Marcando este ítem, se mostrará: No puede hacer que esta parcela aparezca en la búsqueda, porque está situada en una región que lo prohíbe. </notification> <notification name="ClickPublishHelpAvatar"> - Al seleccionar "Mostrar en Buscar" se mostrará: + Al seleccionar 'Mostrar en la búsqueda' se mostrará: - mi perfil en los resultados de la búsqueda - un enlace a mi perfil en las páginas públicas de grupo </notification> @@ -928,7 +931,7 @@ Deberá reconfigurar el nombre y las opciones de la nueva parcela. El color de las parcelas indica el tipo de propietario. Verde = Su terreno -Agua = Terreno de su's grupo's +Agua = Terreno de sus grupos Rojo = Propiedad de otros Amarillo = En venta Morado = Para subasta @@ -1196,13 +1199,13 @@ Por favor, selección sólo uno y reinténtelo. No se han podido configurar las texturas de la región: La textura del terreno [TEXTURE_NUM] tiene una profundidad de bites inválida: [TEXTURE_BIT_DEPTH]. -Cambie la textura [TEXTURE_NUM] por una imagen de 24-bit y 512x512 o menor, y pulse de nuevo "Aplicar" . +Cambie la textura [TEXTURE_NUM] por una imagen de 24-bit y 512x512 o menor, y pulse de nuevo 'Aplicar' . </notification> <notification name="InvalidTerrainSize"> No se han podido configurar las texturas de la región: La textura del terreno [TEXTURE_NUM] es demasiado grande: [TEXTURE_SIZE_X]x[TEXTURE_SIZE_Y]. -Cambie la textura [TEXTURE_NUM] por una imagen de 24-bit y 512x512 o menor, y pulse de nuevo "Aplicar" . +Cambie la textura [TEXTURE_NUM] por una imagen de 24-bit y 512x512 o menor, y pulse de nuevo 'Aplicar' . </notification> <notification name="RawUploadStarted"> Ha empezado la subida. Dependiendo de la velocidad de su conexión, llevará unos dos minutos. @@ -1537,7 +1540,7 @@ Por favor, compruebe que tiene instalado el último visor, y vaya a la Base de C Usted no está autorizado en esa región por su nivel de calificación.. Puede pulsar 'Cambiar preferencia' para aumentar su nivel de calificación y poder entrar. Desde ese momento, podrá buscar y acceder a contenido [REGIONMATURITY]. Si más adelante quiere deshacer esta configuración, vaya a Editar > Preferencias... > General. - <form> + <form name="form"> <button name="OK" text="Cambiar preferencia"/> @@ -1545,7 +1548,7 @@ Puede pulsar 'Cambiar preferencia' para aumentar su nivel de calificac default="true" name="Cancel" text="Cerrar"/> - <ignore text="Cuando la entrada a la región está bloqueada por la preferencia del nivel de calificación"/> + <ignore name="ignore" text="Cuando la entrada a la región está bloqueada por la preferencia del nivel de calificación"/> </form> </notification> <notification name="LandClaimAccessBlocked"> @@ -1735,7 +1738,7 @@ Cuando está activada, los residentes sólo pueden ser empujados por sí mismos Por defecto: Off </notification> <notification label="Unir/Dividir parcelas" name="HelpParcelChanges"> - Esta casilla determina si las parcelas que no son del propietario del estado pueden puede unirse o subdividirse. + Esta casilla determina si las parcelas que no son del propietario del estado pueden unirse o subdividirse. Si no se marca esta opción: * Sólo los propietarios o los administradores del estado pueden unir o dividir parcelas. * Sólo podrán unir o dividir las parcelas pertenecientes al propietario o a un grupo en el que tengan los poderes adecuados. @@ -1860,7 +1863,7 @@ Por defecto: on Esta casilla fija la posición del Sol en la posición del deslizable Fase, y detiene su movimiento. </notification> <notification label="Acceso público" name="HelpEstateExternallyVisible"> - Esta casilla habilita a cualquier residente que esté en otro estado pueda entrar en éste sin tener que estar en una lista de acceso. + Esta casilla habilita que cualquier residente que esté en otro estado pueda entrar en éste sin tener que estar en una lista de acceso. Por defecto: on </notification> @@ -2162,7 +2165,7 @@ Núm. máx. de partículas: define el número máximo de partículas que podrá Calidad del procesamiento: define la resolución con que se renderiza el brillo. -Detalle de la malla: define la cantidad de dettales o número de triángulos usados para renderizar algunos objetos. Cuanto más alto sea el valor, más detalle, pero más tiempo para renderizar. +Detalle de la malla: define la cantidad de detalles o número de triángulos usados para renderizar algunos objetos. Cuanto más alto sea el valor, más detalle, pero más tiempo para renderizar. Detalles de la iluminación: determina que tipo de luces quiere usted que se rendericen. @@ -2195,30 +2198,30 @@ Nivel de detalle del terreno: marca con cuánto detalle quiere ver la textura de <notification name="EnvSettingsHelpButton"> Estas configuraciones ajustan la forma en que usted ve el medio ambiente localmente, en su ordenador. Su tarjeta gráfica debe admitir shaders de la atmósfera ('atmospheric shaders') para poder acceder a esta configuración. -Ajuste el deslizable "Duración de un día" para cambiar localmente, en su visor, las etapas del día. +Ajuste el deslizable 'Duración de un día' para cambiar localmente, en su visor, las etapas del día. -Ajuste el deslizable "Nubosidad" para controlar cuántas nubes cubren el cielo. +Ajuste el deslizable 'Nubosidad' para controlar cuántas nubes cubren el cielo. -Pulse un color en el selector de "Color del agua" para cambiar el color de la misma. +Pulse un color en el selector de 'Color del agua' para cambiar el color de la misma. -Ajuste el deslizable "Claridad del agua" para controlar el nivel de claridad del agua bajo la superficie. +Ajuste el deslizable 'Claridad del agua' para controlar el nivel de claridad del agua bajo la superficie. -Pulse "Usar el horario del estado" para devolver los valores del día al tiempo actual de la región y seguir a partir de él. +Pulse 'Usar el horario del estado' para devolver los valores del día al tiempo actual de la región y seguir a partir de él. -Pulse "Cielo avanzado" para abrir un editor con configuraciones avanzadas para el cielo. +Pulse 'Cielo avanzado' para abrir un editor con configuraciones avanzadas para el cielo. -Pulse "Agua avanzada" para abrir un editor con configuraciones avanzadas para el agua. +Pulse 'Agua avanzada' para abrir un editor con configuraciones avanzadas para el agua. </notification> <notification name="HelpDayCycle"> El Editor del ciclo del día le permite controlar el cielo de Second Life durante el ciclo día/noche. Este es el ciclo que usa el deslizable Duración de un día del Editor del entorno. -El Editor del ciclo del día trabaja configurando fotogramas clave ('keyframes'): nodos (representados por los puntos grises en la línea del tiempo) cada uno de los cuales tiene asociado un Cielo definido. Según progresa la Duración de un día, el WindLight realiza la "animación" del cielo interpolándose entre esos fotogramas clave. +El Editor del ciclo del día trabaja configurando fotogramas clave ('keyframes'): nodos (representados por los puntos grises en la línea del tiempo) cada uno de los cuales tiene asociado un Cielo definido. Según progresa la Duración de un día, el WindLight realiza la 'animación' del cielo interpolándose entre esos fotogramas clave. La flecha amarilla sobre la línea del tiempo representa lo que usted ve actualmente, basándose en la Duración de un día. Púlsela y muévela para ver cómo cambia la animación del día. Puede añadir o borrar fotogramas clave pulsando los botones Añadir un punto o Quitar un punto, situados a la derecha de la línea del tiempo. -Puede establecer la posición en el tiempo de cualquier fotograma clave moviéndolo a lo largo de la línea del tiempo, o configurando manualmente su valor por su valor manualmente en el recuadro Configuración del fotograma clave. También en ese recuadro podrá asociar el fotograma clave a un modelo predefinido de WindLight. +Puede establecer la posición en el tiempo de cualquier fotograma clave moviéndolo a lo largo de la línea del tiempo, o configurando manualmente su valor en el recuadro Configuración del fotograma clave. También en ese recuadro podrá asociar el fotograma clave a un modelo predefinido de WindLight. -La Duración del ciclo establece la duración total de un "día". Marcar un valor bajo (por ejemplo, 2 min.) hará que las 24 horas de su línea del tiempo se animen ¡en sólo dos minutos reales! Una vez que esté satisfecho con su ciclo de la línea del tiempo y los fotogramas clave, utilice los botones Probar y Parar para obtener una vista previa de los resultados. Recuerde que también puede mover la flecha amarilla de encima de la línea del tiempo para ver el ciclo de la animación. El botón Usar el horario del estado sincronizará su ciclo de duración de un día con el ciclo del estado. +La Duración del ciclo establece la duración total de un 'día'. Marcar un valor bajo (por ejemplo, 2 min.) hará que las 24 horas de su línea del tiempo se animen ¡en sólo dos minutos reales! Una vez que esté satisfecho con su ciclo de la línea del tiempo y los fotogramas clave, utilice los botones Probar y Parar para obtener una vista previa de los resultados. Recuerde que también puede mover la flecha amarilla de encima de la línea del tiempo para ver el ciclo de la animación. El botón Usar el horario del estado sincronizará su ciclo de duración de un día con el ciclo del estado. Cuando todo esté a su gusto, puede guardar esos datos y cargarlos luego usando los botones Guardar este tipo de día y Cargar un tipo de día. Note que, por el momento, sólo podemos permitir un ciclo de un día. </notification> @@ -2235,13 +2238,13 @@ Cuando todo esté a su gusto, puede guardar esos datos y cargarlos luego usando La Densidad de la bruma controla lo sombrío de la atmósfera, su neblina. Es eficaz para simular escenas con mucho humo o contaminantes, y también para simular niebla y llovizna. </notification> <notification name="HelpDensityMult"> - La Densidad puede usarse para definir globalmente la 'pesadez' de la atmósfera. Los ajustes bajos dan sensación de un "aire limpio", y los altos de pesadez, de esmog. + La Densidad puede usarse para definir globalmente la 'pesadez' de la atmósfera. Los ajustes bajos dan sensación de un 'aire limpio', y los altos de pesadez, de esmog. </notification> <notification name="HelpDistanceMult"> Ajusta a qué distancia se percibe el WindLight. El valor cero desactiva la influencia del WindLight en el terreno y los objetos. Los valores superiores a 1 simulan mayores distancias a las que afectan los efectos atmosféricos </notification> <notification name="HelpMaxAltitude"> - La Altitud máx. ajusta hasta que altura el WindLight realiza sus cálculos para computar la iluminación atmosférica. En las últimas horas del día, es útil para ajustar la "profundidad" a la que aparece el Sol. + La Altitud máx. ajusta hasta que altura el WindLight realiza sus cálculos para computar la iluminación atmosférica. En las últimas horas del día, es útil para ajustar la 'profundidad' a la que aparece el Sol. </notification> <notification name="HelpSunlightColor"> Ajusta en la escena la intensidad y el color de las luces directas. @@ -2382,7 +2385,7 @@ Pero, vaya, diviértase si quiere. </notification> <notification name="AutoWearNewClothing"> ¿Quiere vestirse automáticamente el ítem de ropa nueva que ha creado? - <usetemplate ignoretext="Vestirme automáticamente la ropa nueva" name="okcancelignore" notext="No" yestext="Sí"/> + <usetemplate ignoretext="Al vestirme automáticamente la ropa nueva" name="okcancelignore" notext="No" yestext="Sí"/> </notification> <notification name="NotAgeVerified"> Para acceder a esta parcela, se debe haber verificado su edad. @@ -2390,7 +2393,7 @@ Pero, vaya, diviértase si quiere. [_URL] <url name="url" option="0"> - https://secondlife.com/account/verification.php + https://secondlife.com/account/verification.php?lang=es </url> <usetemplate ignoretext="Advertir de la falta de la verificación de edad" name="okcancelignore" notext="No" yestext="Sí"/> </notification> @@ -2663,7 +2666,7 @@ Vaya a la 'Help Island Public' ('Isla Pública de Ayuda') pa <notification name="ImproperPaymentStatus"> No tiene el estado de pago adecuado para entrar a esta región. </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> Debe haber verificado su edad para entrar a esta región. </notification> <notification name="MustGetAgeParcel"> @@ -2944,7 +2947,7 @@ Los objetos flexibles no pueden ser materiales, y serán inmateriales hasta que <notification name="FirstDebugMenus"> Ha activado el menú Avanzado. Contiene herramientas útiles para los desarrolladores que trabajan mejorando Second Life. -Para pasar este menú a una ventana, pulse Ctrl-Alt-D. En un Mac, pulse Cmd-Opt-Shift-D. +Para pasar este menú a una ventana, pulse Ctrl-Alt-D. En un Mac, pulse ⌘-Opt-D. </notification> <notification name="FirstSculptedPrim"> Está editando un prim 'sculpted'. diff --git a/indra/newview/skins/default/xui/es/role_actions.xml b/indra/newview/skins/default/xui/es/role_actions.xml index 1e10ccf992..da9a820eb8 100644 --- a/indra/newview/skins/default/xui/es/role_actions.xml +++ b/indra/newview/skins/default/xui/es/role_actions.xml @@ -1,186 +1,199 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> +<?xml version="1.0" encoding="utf-8" standalone="yes" ?> <role_actions> <action_set - description="Estas habilidades incluyen poderes de adicionar o remover miembros del grupo y permitir que nuevos miembros se junten sin una invitación." + description="Estas capacidades incluyen poderes para añadir o quitar miembros del grupo, y para pemitir que se sumen nuevos miembros sin necesidad de invitación." name="Membership"> - <action description="Invitar personas para este grupo" - longdescription="Invite personas para este grupo usando el botón 'Invitar nueva persona...' en Miembros & pestaña Funciones > subpestaña Miembros." + <action description="Invitar personas al grupo" + longdescription="Invitar a personas al grupo usando el botón 'Invitar a un nuevo miembro' en Miembros y Roles > subpestaña Miembros." name="member invite" value="1" /> - <action description="Expulsar a miembros de este grupo" - longdescription="Expulse a miembros de este grupo usando el botón 'Expulsar del grupo' en Miembros & pestaña Funciones > subpestaña Miembros. Un propietario puede expulsar a cualquiera, excepto a otro propietario. Si usted no es un propietario, un miembro puede a expulsarlo del grupo si, y solamente si, él apenas tiene la función de todos y no otras funciones. Para remover miembros de funciones, necesita tener la habilidad 'Remover miembros de funciones'." + <action description="Expulsar a miembros del grupo" + longdescription="Expulsar a miembros del grupo usando el botón 'Expulsar del grupo' en Miembros y Roles > subpestaña Miembros. Un propietario puede expulsar a cualquiera, excepto a otro propietario. Si usted no es un propietario, un miembro del grupo puede expulsarle sólo si tiene concedida tal capacidad específica. Para quitar capacidades a los miembros, usted debe tener la de 'Quitar capacidades a miembros'." name="member eject" value="2" /> <action - description="Activar/desactivar 'Abrir registro' y cambiar 'Tasa de suscripción'" - longdescription="Active/desactive 'Abrir registro' para permitir que nuevos miembros se unan sin una invitación, y cambie 'Tasa de registro' en la sección Preferencia de grupo de la pestaña General." + description="Activar/desactivar 'Inscripción libre' y cambiar 'Cuota de inscripción'" + longdescription="Activar/desactivar 'Inscripción libre' para permitir o no que se unan sin invitación nuevos miembros, y cambiar la 'Cuota de inscripción' en la sección Preferencias del grupo de la pestaña General." name="member options" value="3" /> </action_set> <action_set - description="Estas habilidades incluyen poderes de adicionar, remover y cambiar funciones del grupo; adicionar y remover miembros en funciones y designar habilidades a funciones." + description="Estas habilidades incluyen el poder añadir, quitar y cambiar roles, asignarlos a miembros, y darles capacidades." name="Roles"> - <action description="Crear nuevas funciones" - longdescription="Cree nuevas funciones en Miembros & pestaña Funciones > subpestaña Funciones." + <action description="Crear nuevos roles" + longdescription="Crear nuevos roles en Miembros y Roles > pestaña Roles > botón Crear un rol nuevo." name="role create" value="4" /> - <action description="Borrar funciones" - longdescription="Borra funciones en Miembros & pestaña Funciones > subpestaña Funciones." + <action description="Borrar roles" + longdescription="Borrar roles en Miembros y Roles > pestaña Roles > botón Eliminar el rol." name="role delete" value="5" /> - <action description="Cambiar nombres de funciones, títulos y descripciones" - longdescription="Cambie el nombre de funciones, títulos y descripciones en la parte inferior de Miembros & pestaña Funciones > subpestaña Funciones después de seleccionar una función." + <action description="Cambiar nombres de roles, títulos y descripciones" + longdescription="Cambiar el nombre del rol que elija, su etiqueta y descripción en la parte media de la pestaña Miembros y Roles." name="role properties" value="6" /> - <action description="Designar miembros para la función's del asignador" - longdescription="Designe miembros para funciones en la sección de funciones designadas de Miembros & pestaña Funciones > subpestaña Miembros. Un miembro con este poder puede solamente adicionar miembros para la función que el asignador ya posee." + <action description="Designar miembros para el rol del asignador" + longdescription="Designar miembros para un rol en la sección Roles asignados de la pestaña Miembros y Roles > subpestaña Miembros. Un miembro con este poder sólo puede asignar a otros el rol que él posee." name="role assign member limited" value="7" /> - <action description="Designar miembros para cualquier función" - longdescription="Designe miembros para cualquier función en la sección de funciones designadas de Miembros & pestaña Funciones > subpestaña Miembros. *AVISO* Cualesquiera miembros en una función con esta habilidad pueden designar a sí propios--y cualesquiera otros miembros no propietarios--para funciones que tienen más poderes que las actuales, promocionándolos a poderes próximos al del propietario. Asegúrese de saber lo que está haciendo antes de designar esta habilidad." + <action description="Designar miembros para cualquier rol" + longdescription="Designar miembros para cualquier rol en la sección Roles asignados de la pestaña Miembros y Roles > subpestaña Miembros. *AVISO* Todos los miembros con esta capacidad podrán asignarse a sí mismos -y a otros miembros- roles con mayores poderes de los que actualmente tienen. Potencialmente, podrían elevarse hasta poderes cercanos a los del propietario. Asegúrese de lo que está haciendo antes de otorgar esta capacidad." name="role assign member" value="8" /> - <action description="Remover miembros de las funciones" - longdescription="Remueva miembros de funciones en la sección de funciones designadas de Miembros & pestaña Funciones > subpestaña Miembros. Propietarios no pueden' ser removidos." + <action description="Quitar capacidades a los miembros" + longdescription="Quitar capacidades a los miembros en la sección Capacidades asignadas de Miembros y Roles > subpestaña Roles. No se pueden quitar a los Propietarios." name="role remove member" value="9" /> - <action description="Determinar y remover habilidades en función" - longdescription="Designe y remueva habilidades en funciones en la sección habilidades permitidas de Miembros & pestaña Funciones > subpestaña Funciones. *AVISO* Cualesquiera miembros en una función con esta habilidad pueden designar a sí propios--y cualesquiera otros miembros no propietarios--todas las habilidades, promocionándolos a poderes próximos al del propietario. Asegúrese de saber lo que está haciendo antes de designar esta habilidad." + <action description="Añadir o quitar capacidades a los roles" + longdescription="Añadir o quitar capacidades a los roles en la sección Capacidades asignadas de Miembros y Roles > subpestaña Roles. *AVISO* Todos los miembros con esta capacidad podrán asignarse a sí mismos -y a otros miembros- roles con mayores poderes de los que actualmente tienen. Potencialmente, podrían elevarse hasta poderes cercanos a los del propietario. Asegúrese de lo que está haciendo antes de otorgar esta capacidad." name="role change actions" value="10" /> </action_set> <action_set - description="Estas habilidades incluyen poderes para modificar esta identidad de grupo, como cambiar la visibilidad pública, presentación y insignia." + description="Estas capacidades incluyen poderes para modificar la identidad del grupo, como su visibilidad pública, su carta o su emblema." name="Group Identity"> <action - description="Cambiar presentación, insignia, 'Publicar en la web', y cuales miembros están públicamente visibles en Informaciones del Grupo." - longdescription="Cambie la presentación, insignia, 'Publicar en la web' y cuales miembros están públicamente visibles en Informaciones del grupo. Es hecho en la pestaña General." + description="Cambiar la carta, emblema, 'Mostrar en la búsqueda', y qué miembros serán visibles en la información del grupo" + longdescription="Cambiar la carta, emblema, 'Mostrar en la búsqueda', y qué miembros serán visibles en la información del grupo de la pestaña General." name="group change identity" value="11" /> </action_set> <action_set - description="Estas habilidades incluyen poderes para transferir, modificar y vender terrenos del grupo. Vaya para la ventana Sobre el terreno, haga clic con el botón derecho en el terreno y seleccione 'Sobre el terreno...' o haga clic en la información de la parcela en la barra del menú." + description="Estas capacidades incluyen poderes para transferir, modificar y vender terrenos del grupo. Vea el menú Mundo > Acerca del terreno, o pulse con el botón derecho en el terreno y seleccione 'Acerca del terreno...', o pulse en la información de la parcela en la barra superior del menú." name="Parcel Management"> <action description="Transferir y comprar terreno para el grupo" - longdescription="Transfiere y compre terreno para el grupo. Es hecho en Sobre el terreno > pestaña General." + longdescription="Transferir y comprar terreno para el grupo. Se hace en Acerca del terreno > pestaña General." name="land deed" value="12" /> - <action description="Abandonar al terreno para Gobernador Linden" - longdescription="Abandone al terreno para Gobernador Linden. *AVISO* Cualquier miembro en una función con esta habilidad puede abandonar al terreno perteneciente al grupo en Sobre el terreno > pestaña General, revirtiendo a la posesión Linden sin una venta! Asegúrese de saber lo que está haciendo antes de designar esta habilidad." + <action description="Abandonar al terreno a favor de Governor Linden" + longdescription="Abandonar al terreno a favor de Governor Linden. *AVISO* Todos los miembros con esta capacidad pueden abandonar terreno perteneciente al grupo en Acerca del terreno > pestaña General, devolviendo la posesión a Linden ¡gratuitamente! Asegúrese de lo que está haciendo antes de otorgar esta capacidad." name="land release" value="13" /> - <action description="Definir terreno para información de venta" - longdescription="Defina informaciones de venta para terreno. *AVISO* Cualquier miembro en una función con esta habilidad puede vender terrenos pertenecientes al grupo en Sobre el terreno > pestaña General ¡cómo quiera! Asegúrese de saber lo que está haciendo antes de designar esta habilidad." + <action description="Vender terreno" + longdescription="Vender terreno. *AVISO* Todos los miembros con esta capacidad pueden vender terreno perteneciente al grupo -¡en la forma en que quieran!- en Acerca del terreno > pestaña General. Asegúrese de lo que está haciendo antes de otorgar esta capacidad." name="land set sale info" value="14" /> - <action description="Subdividir y unir parcelas" - longdescription="Subdivide and join parcels. This is done by right-clicking the ground, 'Edit Terrain', and dragging your mouse on the land to make a selection. To subdivide, select what you want to split and click 'Subdivide...'. To join, select two or more contiguous parcels and click 'Join...'. " + <action description="Dividir y unir parcelas" + longdescription="Dividir y unir parcelas. Se hace pulsando con el botón derecho en el terreno, 'Modificar el terreno', y dibujando en el terreno con el ratón lo que se quiere seleccionar. Para dividir, elija la parte que quiere separar y pulse 'Subdividir...'. Para unir, seleccione dos o más parcelas contiguas y pulse 'Unir...'. " name="land divide join" value="15" /> </action_set> <action_set - description="Estas habilidades incluyen poderes para cambiar el nombre de las parcelas y configuraciones de publicación, visibilidad de la búsqueda de directorio y punto de aterrizaje & opciones de ruta de TP." + description="Estas capacidades incluyen poder cambiar el nombre de la parcela y su configuración, así como si se muestra en Buscar y las opciones del punto de llegada y el de teleporte." name="Parcel Identity"> <action - description="Activar/desactivar 'Exhibir en locales de encuentro' y definir categoría" - longdescription="Activar/desactivar 'Exhibir en locales de encuentro' y configurar una categoría de parcela en Sobre el terreno > pestaña Opciones." + description="Activar/desactivar 'Mostrar en Buscar' y el definir la categoría" + longdescription="Activar/desactivar el 'Mostrar en Buscar' y en que categoría se mostrará en Acerca del terreno > pestaña Opciones." name="land find places" value="17" /> <action - description="Cambiar nombre de la parcela, descripción, y configuraciones 'Publicar en la web'" - longdescription="Cambie el nombre de la parcela, descripción y configuraciones de 'Publicar en la web'. Es hecho en Sobre el terreno > pestaña Opciones." + description="Cambiar el nombre de la parcela, la descripción, y la configuración de 'Mostrar en Buscar'" + longdescription="Cambiar el nombre y descripción de la parcela, y la configuración de 'Mostrar en Buscar'. Se hace en Acerca del terreno > pestaña Opciones." name="land change identity" value="18" /> - <action description="Definir punto de aterrizaje y ruta de teletransporte" - longdescription="En una parcela perteneciente al grupo, miembros en una función con esta habilidad pueden definir un punto de aterrizaje para especificar donde los teletransportes llegan y también definir la ruta del teletransporte para un mayor control. Es hecho en Sobre el terreno > pestaña Opciones." + <action description="Definir los puntos de llegada y teleporte" + longdescription="En una parcela perteneciente al grupo, los miembros con un rol que tenga esta capacidad pueden precisar el punto de llegada o el de teleporte. Se hace en Acerca del terreno > pestaña Opciones." name="land set landing point" value="19" /> </action_set> <action_set - description="Estas habilidades incluyen poderes que afectan opciones de parcela, como 'Crear objetos', 'Editar terreno' y música & configuraciones de multimedia." + description="Estas capacidades incluyen poderes que afectan a las opciones de la parcela, como 'Crear objetos', 'Editar el terreno' y las configuraciones de la música y los media." name="Parcel Settings"> - <action description="Cambiar música & configuraciones de multimedia" - longdescription="Cambie streaming de música y configuraciones de vídeo en Sobre el terreno > pestaña Multimedia." + <action description="Cambiar música y configuraciones de los media" + longdescription="Cambiar la música en streaming y las configuraciones de vídeo en Acerca del terreno > pestaña Media." name="land change media" value="20" /> - <action description="Activar/desactivar 'Editar terreno'" - longdescription="Active/desactive 'Editar terreno'. *AVISO* Sobre el terreno > pestaña Opciones > Editar terreno permite a cualquiera alterar las formas de su terreno', sustituir y mover plantas Linden. Asegúrese de saber lo que está haciendo antes de designar esta habilidad. La edición de terreno es activada/desactivada en Sobre el terreno > pestaña Opciones." + <action description="Activar/desactivar 'Editar el terreno'" + longdescription="Activar/desactivar 'Editar el terreno'. *AVISO* Acerca del terreno > pestaña Opciones > Editar el terreno, permite a cualquiera alterar la forma de su terreno y sustituir y mover plantas Linden. Asegúrese de lo que está haciendo antes de otorgar esta capacidad. La edición del terreno se activada/desactiva en Acerca del terreno > pestaña Opciones." name="land edit" value="21" /> <action - description="Activar/desactivar variados Sobre el Terreno > Opciones de configuraciones" - longdescription="Active/desactive 'Seguro (sin daño)', 'Volar', y permita a otros residentes: 'Crear objetos', 'Editar terreno', 'Crear puntos de referencia', y 'Ejecutar scripts' en un terreno perteneciente al grupo en Sobre el terreno > pestaña Opciones." + description="Activar/desactivar varios ítems de Acerca del terreno > Opciones" + longdescription="Activar/desactivar en un terreno del grupo los ítems de Acerca del terreno > pestaña Opciones: 'Seguro (sin daño)', 'Volar', y permitir a otros residentes: 'Crear objetos', 'Editar el terreno', 'Crear hitos', y 'Ejecutar scripts'." name="land options" value="22" /> </action_set> <action_set - description="Estas habilidades incluyen poderes que permiten a miembros rebasar restricciones en parcelas pertenecientes al grupo." + description="Estas capacidades incluyen poderes que permiten a los miembros rebasar las restricciones de parcelas pertenecientes al grupo." name="Parcel Powers"> - <action description="Siempre permitir 'Editar terreno'" - longdescription="Miembros en una función con esta habilidad pueden editar terreno en una parcela perteneciente al grupo, mismo si está desactivada en Sobre el terreno > pestaña Opciones." + <action description="Permitir siempre 'Editar el terreno'" + longdescription="Quien tenga un rol con esta capacidad puede editar el terreno de una parcela perteneciente al grupo aunque eso esté desactivado en Acerca del terreno > pestaña Opciones." name="land allow edit land" value="23" /> - <action description="Siempre permitir 'Volar'" - longdescription="Miembros en una función con esta habilidad pueden volar sobre una parcela perteneciente al grupo, mismo si está desactivada en Sobre el terreno > pestaña Opciones." + <action description="Permitir siempre 'Volar'" + longdescription="Quien tenga un rol con esta capacidad puede volar sobre una parcela perteneciente al grupo aunque eso esté desactivado en Acerca del terreno > pestaña Opciones." name="land allow fly" value="24" /> - <action description="Siempre permitir 'Crear objetos'" - longdescription="Miembros en una función con esta habilidad pueden crear objetos en una parcela perteneciente al grupo, mismo si está desactivada en Sobre el terreno > pestaña Opciones." + <action description="Permitir siempre 'Crear objetos'" + longdescription="Quien tenga un rol con esta capacidad puede crear objetos en una parcela perteneciente al grupo aunque eso esté desactivado en Acerca del terreno > pestaña Opciones." name="land allow create" value="25" /> - <action description="Siempre permitir 'Crear punto de referencia'" - longdescription="Miembros en una función con esta habilidad pueden poner un punto de referencia en una parcela perteneciente al grupo, mismo si está desactivada en Sobre el terreno > pestaña Opciones." + <action description="Permitir siempre 'Crear hitos'" + longdescription="Quien tenga un rol con esta capacidad puede crear un hito en una parcela perteneciente al grupo aunque eso esté desactivado en Acerca del terreno > pestaña Opciones." name="land allow landmark" value="26" /> - <action description="Permitir 'Colocar casa aquí' en el terreno del grupo" - longdescription="Miembros en una función con esta habilidad pueden usar el menú Mundo > Definir hogar aquí en una parcela del grupo (definir terreno o transferir para este grupo)." + <action description="Permitir 'Fijar mi Base aquí' en el terreno del grupo" + longdescription="Quien tenga un rol con esta capacidad puede usar el menú Mundo > Fijar mi Base aquí en una parcela transferida al grupo." name="land allow set home" value="28" /> </action_set> <action_set - description="Estas habilidades incluyen poderes de permitir o restringir acceso a parcelas pertenecientes al grupo, incluyendo congelar y expulsar a residentes." + description="Estas capacidades incluyen poderes para permitir o restringir el acceso a parcelas pertenecientes al grupo, incluyendo el congelar y expulsar a residentes." name="Parcel Access"> - <action description="Administrar listas de acceso a la parcela" - longdescription="Administre la lista de acceso a la parcela en Sobre el terreno > pestaña Acceso." + <action description="Administrar las listas de acceso a la parcela" + longdescription="Administre las listas de acceso a la parcela en Acerca del terreno > pestaña Acceso." name="land manage allowed" value="29" /> - <action description="Administrar lista de desterrados de la parcela" - longdescription="Administre la lista de desterrados de la parcela en Sobre el terreno > pestaña Desterrado." + <action description="Administrar la lista de residentes con el acceso prohibido" + longdescription="Administrar la lista de residentes con el acceso prohibido a la parcela en Acerca del terreno > pestaña Acceso." name="land manage banned" value="30" /> - <action description="Cambiar configuraciones de parcela 'Vender pases...'" - longdescription="Cambie configuraciones de 'Vender pases...' en Sobre el terreno > pestaña Acceso." + <action description="Cambiar en las configuraciones de parcela el 'Vender pases a...'" + longdescription="Cambiar la configuración de 'Vender pases a...' en Acerca del terreno > pestaña Acceso." name="land manage passes" value="31" /> <action description="Expulsar y congelar residentes en las parcelas" - longdescription="Miembros en una función con esta habilidad pueden lidiar con un residente indeseado en una parcela perteneciente al grupo haciendo clic con el botón derecho sobre él, Más > y seleccionando 'Expulsar...' o 'Congelar...'." + longdescription="Quien tenga un rol con esta capacidad puede actuar frente a un residente indeseado en una parcela del grupo pulsando con el botón derecho sobre él, Más > y seleccionando 'Expulsar...' o 'Congelar...'." name="land admin" value="32" /> </action_set> <action_set - description="Estas habilidades incluyen poderes de permitir a miembros devolver objetos y poner y mover plantas Linden. Útil para que miembros organicen el paisaje, pero debe ser usado con cuidado, debido a no ser posible deshacer la mudanza de los objetos." + description="Estas capacidades incluyen poderes que permitan a los miembros devolver objetos y poner y mover plantas Linden. Es útil para que miembros organicen el paisaje, pero debe ser usado con cuidado, ya que no se pueden deshacer esos cambios en los objetos." name="Parcel Content"> <action description="Devolver objetos que pertenecen al grupo" - longdescription="Devuelva objetos en parcelas pertenecientes al grupo que pertenecen al grupo en Sobre el terreno > pestaña Objetos." + longdescription="Devolver objetos pertenecientes al grupo en parcelas de su propiedad en Acerca del terreno > pestaña Objetos." name="land return group owned" value="48" /> <action description="Devolver objetos definidos para el grupo" - longdescription="Devuelva objetos en parcelas pertenecientes al grupo en Sobre el terreno > pestaña Objetos." + longdescription="Devuelva objetos en parcelas pertenecientes al grupo en Acerca del terreno > pestaña Objetos." name="land return group set" value="33" /> <action description="Devolver objetos que no pertenecen al grupo" - longdescription="Devuelva objetos en las parcelas pertenecientes a un grupo que está sin grupo en el Sobre el terreno > pestaña Objetos." + longdescription="Devuolver objetos que estén en una parcela del grupo y pertenezcan a alguien que no sea del grupo en Acerca del terreno > pestaña Objetos." name="land return non group" value="34" /> - <action description="Enjardinar usando plantas Linden" - longdescription="La habilidad de enjardinar permite poner y mover árboles Linden, plantas y céspedes. Estos ítems pueden ser encontrados en la's Biblioteca de su inventario > carpeta Objetos o pueden ser criados a través del botón Construir." + <action description="Modificar el paisaje usando plantas Linden" + longdescription="La capacidad de modificar el paisaje permite poner y mover árboles Linden, plantas y arbustos. Estos ítems están en la 's Biblioteca de su Inventario > carpeta Objetos, o pueden crearse con el botón Construir." name="land gardening" value="35" /> </action_set> <action_set - description="These Abilities include powers to deed, modify, and sell group-owned objects. These changes are done in the Edit Tools > General Tab. Right-click an object and Edit to see its settings. " + description="Estas capacidades incluyen poderes para tranferir, modificar y vender objetos pertenecientes al grupo. Estos cambios se hacen en la pestaña General de la herramienta de edición. Para verlo, pulse en un objeto con el botón derecho y elija Editar. " name="Object Management"> - <action description="Transferir objetos para el grupo" - longdescription="Transfiere objetos para el grupo en Editar herramientas > pestaña General." + <action description="Transferir objetos al grupo" + longdescription="Transferir objetos al grupo en Editar > pestaña General." name="object deed" value="36" /> - <action description="Manipular (mover, copiar, modificar) objetos del grupo" - longdescription="Manipule (mover,copiar, modificar) objetos pertenecientes al grupo en Editar Herramientas > pestaña General." + <action description="Manipular (mover, copiar, y modificar) objetos del grupo" + longdescription="Manipular (mover, copiar, y modificar) objetos pertenecientes al grupo en Editar > pestaña General." name="object manipulate" value="38" /> - <action description="Definir objetos pertenecientes al grupo para venta" - longdescription="Defina objetos pertenecientes al grupo para venta en Editar Herramientas > pestaña General." + <action description="Vender objetos pertenecientes al grupo" + longdescription="Poner a la venta objetos pertenecientes al grupo para venta en Editar > pestaña General." name="object set sale" value="39" /> </action_set> <action_set - description="Estas habilidades incluyen poderes que requieren que miembros paguen deudas y reciban dividendos del grupo, y restringen acceso al historial de cuenta del grupo." + description="Estas habilidades incluyen poderes para que los miembros paguen deudas del grupo o reciban sus dividendos, y para limitar el acceso al historial de la cuenta del grupo." name="Accounting"> - <action description="Pagar débitos y reciber dividendos del grupo" - longdescription="Members in a Role with this Ability will automatically pay group liabilities and receive group dividends. This means they will receive a portion of group-owned land sales which are distributed daily, as well as contribute towards things like parcel listing fees. " + <action description="Pagar deudas y recibir dividendos del grupo" + longdescription="Quien tenga un rol con esta capacidad, automáticamente pagará deudas del grupo y recibirá sus dividendos. Esto significa que recibirá una parte de las ventas de terreno de grupo, y que contribuirá a cosas como, por ejemplo, las cuotas por posesión de terreno. " name="accounting accountable" value="40" /> </action_set> <action_set - description="Estas habilidades incluyen poderes de permitir enviar, recibir y ver avisos de grupo." + description="Estas habilidades incluyen poderes para enviar, recibir y ver avisos de grupo." name="Notices"> <action description="Enviar aviso" - longdescription="Miembros en una función con esta habilidad pueden enviar avisos en Informaciones de grupo > pestaña Avisos." + longdescription="Quien tenga un rol con esta capacidad puede enviar avisos en Información del grupo > pestaña Avisos." name="notices send" value="42" /> - <action description="Reciber nuevos avisos y ver los anteriores" - longdescription="Miembros en una función con esta habilidad pueden recibir los nuevos avisos y ver los anteriores en Informaciones de grupo > pestaña Avisos." + <action description="Recibir avisos nuevos y ver los anteriores" + longdescription="Quien tenga un rol con esta capacidad puede recibir los avisos nuevos, y ver los anteriores en Información del grupo > pestaña Avisos." name="notices receive" value="43" /> </action_set> <action_set - description="Estas habilidades incluyen poderes de permitir a miembros definir y votar en propuestas y ver historial de votación." + description="Estas habilidades incluyen poderes para permitir a los miembros crear propuestas, votarlas, y ver el historial de votaciones." name="Proposals"> - <action description="Crear propuesta" - longdescription="Miembros en una función con esta habilidad pueden crear propuestas para que sean votadas en Informaciones de grupo > pestaña Propuestas." + <action description="Hacer una propuesta" + longdescription="Quien tenga un rol con esta capacidad puede crear propuestas para que sean votadas en Información del grupo > pestaña Propuestas." name="proposal start" value="44" /> <action description="Votar en propuestas" - longdescription="Miembros en una función con esta habilidad pueden votar en propuestas en Informaciones de grupo > pestaña Propuestas." + longdescription="Quien tenga un rol con esta capacidad puede votar las propuestas en Información del grupo > pestaña Propuestas." name="proposal vote" value="45" /> </action_set> + <action_set + description="Estas capacidades incluyen poderes para permitir o no el aceso a las sesiones de chat del grupo y al chat de voz del mismo." + name="Chat"> + <action description="Abrir chat de grupo" + longdescription="Quien tenga un rol con esta capacidad puede abrir sesiones de chat del grupo, tanto de texto como de voz." + name="join group chat" value="16" /> + <action description="Abrir chat de voz del grupo" + longdescription="Quien tenga un rol con esta capacidad puede abrir sesiones de chat de voz del grupo. NOTA: para acceder al chat de voz debe tenerse la capacidad 'Abrir chat de grupo'." + name="join voice chat" value="27" /> + <action description="Moderar el chat de grupo" + longdescription="Quien tenga esta capacidad puede controlar el acceso y la participación en los chats de texto y de voz del grupo." + name="moderate group chat" value="37" /> + </action_set> </role_actions> diff --git a/indra/newview/skins/default/xui/es/strings.xml b/indra/newview/skins/default/xui/es/strings.xml index 4793f6c661..96bf0ea865 100644 --- a/indra/newview/skins/default/xui/es/strings.xml +++ b/indra/newview/skins/default/xui/es/strings.xml @@ -55,6 +55,9 @@ <string name="LoginDownloadingClothing"> Descargando la ropa... </string> + <string name="Quit"> + Salir + </string> <string name="AgentLostConnection"> Esta región puede estar teniendo problemas. Por favor, compruebe su conexión a internet. </string> diff --git a/indra/newview/skins/default/xui/fr/floater_world_map.xml b/indra/newview/skins/default/xui/fr/floater_world_map.xml index d7ffe9205d..bb5738be27 100644 --- a/indra/newview/skins/default/xui/fr/floater_world_map.xml +++ b/indra/newview/skins/default/xui/fr/floater_world_map.xml @@ -66,6 +66,6 @@ <button label="Afficher la destination" label_selected="Afficher la destination" name="Show Destination" tool_tip="Centrer la carte sur l'endroit sélectionné" width="165"/> <button label="Effacer" label_selected="Effacer" left="-270" name="Clear" tool_tip="Arrêter de suivre"/> <button label="Afficher mon emplacement" label_selected="Afficher mon emplacement" name="Show My Location" tool_tip="Centrer la carte sur l'emplacement de votre avatar" width="165"/> - <button label="Copier la SLURL dans le presse-papiers" left="-270" name="copy_slurl" tool_tip="Copier l'emplacement actuel comme SLURL pour l'utiliser sur le Web." width="262"/> + <button label="Copier la SLurl dans le presse-papiers" left="-270" name="copy_slurl" tool_tip="Copier l'emplacement actuel comme SLurl pour l'utiliser sur le Web." width="262"/> <slider label="Zoom" left="-270" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/fr/notifications.xml b/indra/newview/skins/default/xui/fr/notifications.xml index ac8d0cc605..7d2cdd8454 100644 --- a/indra/newview/skins/default/xui/fr/notifications.xml +++ b/indra/newview/skins/default/xui/fr/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> Toujours choisir cette option </global> + <global name="implicitclosebutton"> + Fermer + </global> <template name="okbutton"> <form> <button @@ -2068,12 +2071,12 @@ Souhaitez-vous quitter le mode occupé avant de terminer cette transaction ? <usetemplate ignoretext="Losque vous videz le dossier Objets trouvés dans votre inventaire" name="okcancelignore" notext="Non" yestext="Oui"/> </notification> <notification name="CopySLURL"> - La SLURL suivante a été copiée dans votre presse-papiers : + La SLurl suivante a été copiée dans votre presse-papiers : [SLURL] Mettez-la dans une page web pour permettre aux autres résidents d'accéder facilement à cet endroit ou bien collez-la dans la barre d'adresse de votre navigateur. <form name="form"> - <ignore name="ignore" text="Lorsque vous copiez une SLURL dans votre presse-papier"/> + <ignore name="ignore" text="Lorsque vous copiez une SLurl dans votre presse-papier"/> </form> </notification> <notification name="GraphicsPreferencesHelp"> @@ -2595,7 +2598,7 @@ Pour répéter le didacticiel, veuillez aller sur Help Island Public. <notification name="ImproperPaymentStatus"> Vous n'avez pas le statut de paiement approprié pour pénétrer dans cette région. </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> Pour pouvoir pénétrer dans cette région, vous devez avoir procédé à la vérification de votre âge. </notification> <notification name="MustGetAgeParcel"> @@ -2877,7 +2880,7 @@ Les objets flexibles ne peuvent pas avoir de propriétés physiques et doivent r <notification name="FirstDebugMenus"> Vous avez activé le menu Avancé. Ce menu contient des fonctionnalités utiles pour les développeurs qui travaillent sur Second Life. -Pour activer/désactiver ce menu sous Windows, appuyez sur Ctrl-Alt-D. Sur un Mac, appuyez sur Cmd-Opt-Maj-D +Pour activer/désactiver ce menu sous Windows, appuyez sur Ctrl-Alt-D. Sur un Mac, appuyez sur ⌘-Opt-Maj-D </notification> <notification name="FirstSculptedPrim"> Vous êtes en train d'éditer un sculptie. diff --git a/indra/newview/skins/default/xui/it/floater_tools.xml b/indra/newview/skins/default/xui/it/floater_tools.xml index a89ae9d803..3c79cd8829 100644 --- a/indra/newview/skins/default/xui/it/floater_tools.xml +++ b/indra/newview/skins/default/xui/it/floater_tools.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="utf-8" standalone="yes"?> -<floater name="toolbox floater" title=""> +<floater name="toolbox floater" title="" short_title="Costruisci" width="288"> <button label="" label_selected="" name="button focus" tool_tip="Focus"/> <button label="" label_selected="" name="button move" tool_tip="Muoviti"/> <button label="" label_selected="" name="button edit" tool_tip="Modifica"/> <button label="" label_selected="" name="button create" tool_tip="Crea"/> <button label="" label_selected="" name="button land" tool_tip="Terra"/> <check_box label="Zoom" name="radio zoom"/> - <check_box label="Guarda ruotando(Ctrl)" name="radio orbit"/> + <check_box label="Guarda ruotando (Ctrl)" name="radio orbit"/> <check_box label="Guarda panoramicamente (Ctrl-Shift)" name="radio pan"/> <check_box label="Muovi" name="radio move"/> <check_box label="Alza (Ctrl)" name="radio lift"/> @@ -17,9 +17,9 @@ <check_box label="Seleziona Texture" name="radio select face"/> <check_box label="Modifica parti unite" name="checkbox edit linked parts"/> <text name="text ruler mode"> - Modalità regolo di precisione: + Modalità: </text> - <combo_box name="combobox grid mode"> + <combo_box name="combobox grid mode" left_delta="48"> <combo_item name="World"> Globale </combo_item> @@ -30,11 +30,11 @@ Riferito a </combo_item> </combo_box> - <check_box label="Ridimensiona simmetricamente" name="checkbox uniform"/> - <check_box label="Ridimensiona le Texture" name="checkbox stretch textures"/> + <check_box label="Ridimens. simmetricamente" name="checkbox uniform"/> + <check_box label="Ridimensiona le texture" name="checkbox stretch textures"/> <check_box label="Usa righello" name="checkbox snap to grid"/> <button label="Opzioni..." label_selected="Opzioni..." name="Options..."/> - <text name="text status"> + <text name="text status" width="280"> Trascina per muovere, trascina+maiuscolo per copiare </text> <button label="" label_selected="" name="ToolCube" tool_tip="Cubo"/> @@ -63,23 +63,24 @@ <check_box label="Uniforma" name="radio smooth"/> <check_box label="Ondula" name="radio noise"/> <check_box label="Ripristina" name="radio revert"/> - <button label="Applica" label_selected="Applica" name="button apply to selection" tool_tip="Modifica il terreno selezionato"/> + <button label="Applica" label_selected="Applica" name="button apply to selection" tool_tip="Modifica il terreno selezionato" left="146"/> <text name="Bulldozer:"> Bulldozer: </text> <text name="Dozer Size:"> Grandezza </text> + <volume_slider left="184" name="slider brush size" width="74" /> <text name="Strength:"> Potenza </text> - <text name="obj_count"> + <text name="obj_count" left="134"> Oggetti selezionati: [COUNT] </text> - <text name="prim_count"> - primitive: [COUNT] + <text name="prim_count" left="134"> + primitivi: [COUNT] </text> - <tab_container name="Object Info Tabs"> + <tab_container name="Object Info Tabs" tab_max_width="150" tab_min_width="30" width="288"> <panel label="Generale" name="General"> <text name="Name:"> Nome: @@ -121,7 +122,7 @@ <string name="text deed"> Cedi al gruppo </string> - <button label="Cedi al gruppo..." label_selected="Cedi al gruppo..." name="button deed" tool_tip="Gli oggetti condivisi con il gruppo possono essere ceduti da un funzionario del gruppo."/> + <button left_delta="152" width="98" label="Cedi al gruppo..." label_selected="Cedi al gruppo..." name="button deed" tool_tip="Gli oggetti condivisi con il gruppo possono essere ceduti da un funzionario del gruppo."/> <check_box label="Permetti a chiunque di spostare" name="checkbox allow everyone move"/> <check_box label="Permetti a chiunque di copiare" name="checkbox allow everyone copy"/> <check_box label="Mostra nella ricerca" name="search_check" tool_tip="Permetti che l'oggetto sia visibile nella ricerca"/> @@ -144,12 +145,12 @@ Il prossimo proprietario può: </text> <check_box label="Modificare" name="checkbox next owner can modify"/> - <check_box label="Copiare" name="checkbox next owner can copy"/> - <check_box label="Rivendere/Regalare" name="checkbox next owner can transfer"/> - <text name="label click action"> - Se cliccato con il tasto sinistro del mouse + <check_box label="Copiare" name="checkbox next owner can copy" left_delta="80"/> + <check_box label="Rivendere/Regalare" name="checkbox next owner can transfer" left_delta="67"/> + <text name="label click action" width="220"> + Se cliccato con il tasto sinistro del mouse: </text> - <combo_box name="clickaction"> + <combo_box name="clickaction" width="192"> <combo_item name="Touch/grab(default)"> Tocca/Afferra (default) </combo_item> @@ -176,7 +177,7 @@ B: </text> <text name="O:"> - O; + O: </text> <text name="G:"> G: @@ -206,7 +207,7 @@ Devi selezionare l'intero oggetto per impostare i permessi. </string> <string name="Cost Default"> - Prezzo: L$ + Prezzo: L$ </string> <string name="Cost Total"> Prezzo totale: L$ @@ -351,7 +352,7 @@ </text> <spinner label="X" name="Shear X"/> <spinner label="Y" name="Shear Y"/> - <text name="advanced_cut"> + <text name="advanced_cut" width="149"> Ritaglia il profilo Inizio e Fine </text> <text name="advanced_dimple"> @@ -398,29 +399,29 @@ </combo_box> </panel> <panel label="Caratteristiche" name="Features"> - <text name="select_single"> + <text name="select_single" width="280"> Seleziona solo un prim per modificarne le caratteristiche. </text> <text name="edit_object"> Modifica le caratteristiche dell'oggetto: </text> <check_box label="Flessibilità" name="Flexible1D Checkbox Ctrl" tool_tip="Permette all'oggetto di flettersi rispetto all'asse Z. (solo lato client)"/> - <spinner label="Morbidezza" name="FlexNumSections"/> - <spinner label="Gravità" name="FlexGravity"/> - <spinner label="Elasticità" name="FlexFriction"/> - <spinner label="Sventolio" name="FlexWind"/> - <spinner label="Tensione" name="FlexTension"/> - <spinner label="Forza X" name="FlexForceX"/> - <spinner label="Forza Y" name="FlexForceY"/> - <spinner label="Forza Z" name="FlexForceZ"/> + <spinner label="Morbidezza" name="FlexNumSections" label_width="72" width="135"/> + <spinner label="Gravità" name="FlexGravity" label_width="72" width="135"/> + <spinner label="Elasticità" name="FlexFriction" label_width="72" width="135"/> + <spinner label="Sventolio" name="FlexWind" label_width="72" width="135"/> + <spinner label="Tensione" name="FlexTension" label_width="72" width="135"/> + <spinner label="Forza X" name="FlexForceX" label_width="72" width="135"/> + <spinner label="Forza Y" name="FlexForceY" label_width="72" width="135"/> + <spinner label="Forza Z" name="FlexForceZ" label_width="72" width="135"/> <check_box label="Luce" name="Light Checkbox Ctrl" tool_tip="Imposta l'oggetto come sorgente di luce"/> <text name="label color"> Colore </text> <color_swatch label="" name="colorswatch" tool_tip="Clicca per aprire la tavolozza dei colori"/> - <spinner label="Intensità" name="Light Intensity"/> - <spinner label="Raggio" name="Light Radius"/> - <spinner label="Attenuazione" name="Light Falloff"/> + <spinner label="Intensità" name="Light Intensity" label_width="72" width="135"/> + <spinner label="Raggio" name="Light Radius" label_width="72" width="135"/> + <spinner label="Attenuazione" name="Light Falloff" label_width="72" width="135" /> </panel> <panel label="Texture" name="Texture"> <texture_picker label="Texture" name="texture control" tool_tip="Clicca per scegliere un'immagine"/> @@ -431,11 +432,11 @@ <text name="glow label"> Bagliore </text> - <check_box label="Massima luminosità" name="checkbox fullbright"/> + <check_box label="Massima luminosità" name="checkbox fullbright" bottom_delta="-21"/> <text name="tex gen"> - Applicazione della texture + Applicazione della texture </text> - <combo_box name="combobox texgen"> + <combo_box name="combobox texgen" bottom_delta="-38"> <combo_item name="Default"> Default </combo_item> @@ -443,10 +444,10 @@ Planare </combo_item> </combo_box> - <text name="label shininess"> + <text name="label shininess" bottom="-120"> Brillantezza </text> - <combo_box name="combobox shininess"> + <combo_box name="combobox shininess" bottom_delta="-22"> <combo_item name="None"> Nessuna </combo_item> @@ -460,10 +461,10 @@ Alta </combo_item> </combo_box> - <text name="label bumpiness"> + <text name="label bumpiness" bottom="-120"> Rilievo </text> - <combo_box name="combobox bumpiness"> + <combo_box name="combobox bumpiness" width="100" bottom_delta="-22"> <combo_item name="None"> Nessuna </combo_item> @@ -538,7 +539,9 @@ <text name="rpt"> Ripetizioni per metro </text> - <button label="Applica" label_selected="Applica" name="button apply"/> + <spinner left="120" name="TexRot" width="60" /> + <spinner left="120" name="rptctrl" width="60" /> + <button label="Applica" label_selected="Applica" name="button apply" left_delta="72"/> <text name="tex offset"> Offset </text> @@ -548,11 +551,12 @@ Allinea texture dei media (deve prima caricarsi) </text> - <button label="Allinea" label_selected="Allinea" name="button align"/> + <button label="Allinea" label_selected="Allinea" name="button align" left="160"/> </panel> <panel label="Contenuto" name="Contents"> <button label="Nuovo Script" label_selected="Nuovo script..." name="button new script"/> <button label="Permessi..." name="button permissions"/> + <panel name="ContentsInventory" width="272" /> </panel> </tab_container> <panel name="land info panel"> @@ -560,24 +564,24 @@ Informazioni sul terreno </text> <text name="label_area_price"> - Prezzo: [PRICE] L$ per [AREA] mq. + Prezzo: [PRICE] L$ per [AREA] m² </text> <text name="label_area"> - Area: [AREA] mq. + Area: [AREA] m² </text> - <button label="Informazioni sul terreno..." label_selected="Informazioni sul terreno..." name="button about land"/> + <button label="Informazioni sul terreno..." label_selected="Informazioni sul terreno..." name="button about land" width="156"/> <check_box label="Mostra i proprietari" name="checkbox show owners" tool_tip="Colora i terreni in base ai loro proprietari"/> - <button label="?" label_selected="?" name="button show owners help"/> + <button label="?" label_selected="?" name="button show owners help" left_delta="120"/> <text name="label_parcel_modify"> Modifica il terreno </text> - <button label="Suddividi..." label_selected="Suddividi..." name="button subdivide land"/> - <button label="Unisci..." label_selected="Unisci..." name="button join land"/> + <button label="Suddividi..." label_selected="Suddividi..." name="button subdivide land" width="156"/> + <button label="Unisci..." label_selected="Unisci..." name="button join land" width="156"/> <text name="label_parcel_trans"> Transazioni del territorio </text> - <button label="Acquista il terreno..." label_selected="Acquista il terreno..." name="button buy land"/> - <button label="Abbandona il terreno..." label_selected="Abbandona il terreno..." name="button abandon land"/> + <button label="Acquista il terreno..." label_selected="Acquista il terreno..." name="button buy land" width="156"/> + <button label="Abbandona il terreno..." label_selected="Abbandona il terreno..." name="button abandon land" width="156"/> </panel> <string name="status_rotate"> Sposta le fasce colorate per ruotare l'oggetto diff --git a/indra/newview/skins/default/xui/it/floater_world_map.xml b/indra/newview/skins/default/xui/it/floater_world_map.xml index 088c8a7189..9b2fc5aff1 100644 --- a/indra/newview/skins/default/xui/it/floater_world_map.xml +++ b/indra/newview/skins/default/xui/it/floater_world_map.xml @@ -54,6 +54,6 @@ <button font="SansSerifSmall" left_delta="91" width="135" label="Mostra destinazione" label_selected="Mostra destinazione" name="Show Destination" tool_tip="Centra la mappa sul luogo prescelto"/> <button font="SansSerifSmall" label="Pulisci" label_selected="Pulisci" name="Clear" tool_tip="Togli traccia"/> <button font="SansSerifSmall" left_delta="91" width="135" label="Mostra la mia posizione" label_selected="Mostra la mia posizione" name="Show My Location" tool_tip="Centra la mappa alla posizione del tuo avatar"/> - <button font="SansSerifSmall" label="Copia lo SLURL negli appunti" name="copy_slurl" tool_tip="Copia l'attuale posizione quale SLURL utilizzabile nel web."/> + <button font="SansSerifSmall" label="Copia lo SLurl negli appunti" name="copy_slurl" tool_tip="Copia l'attuale posizione quale SLurl utilizzabile nel web."/> <slider label="Zoom" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/it/notifications.xml b/indra/newview/skins/default/xui/it/notifications.xml index 786bfdf7ef..9e54bc24a9 100644 --- a/indra/newview/skins/default/xui/it/notifications.xml +++ b/indra/newview/skins/default/xui/it/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> Scegli sempre questa opzione </global> + <global name="implicitclosebutton"> + Chiudi + </global> <template name="okbutton"> <form> <button @@ -174,7 +177,7 @@ Vuoi davvero dare i diritti di modifica ai residenti selezionati? Non si possono rimuovere membri da quel ruolo. I membri devono dimettersi volontariamente dal ruolo. Confermi l'operazione? - <usetemplate ignoretext="Quando si aggiungono membri al ruolo di proprietario" name="okcancelignore" notext="No" yestext="Si"/> + <usetemplate ignoretext="Quando si aggiungono membri al ruolo di proprietario del gruppo." name="okcancelignore" notext="No" yestext="Si"/> </notification> <notification name="AssignDangerousActionWarning"> Stai per aggiungere il potere '[ACTION_NAME]' al ruolo '[ROLE_NAME]'. @@ -195,7 +198,7 @@ Aggiungi questo potere a '[ROLE_NAME]'? <usetemplate name="okcancelbuttons" notext="No" yestext="Si"/> </notification> <notification name="ClickPublishHelpLand"> - Selezionare "Pubblica in Ricerca" + Selezionare 'Pubblica in Ricerca' Marcando questo campo si mostrerà: - questo terreno nei risultati di ricerca - gli oggetti pubblici di questo terreno @@ -225,7 +228,7 @@ Marcando questo campo si mostrerà: Non puoi rendere questo terreno visibile nella ricerca perchè è in una regione che non lo consente. </notification> <notification name="ClickPublishHelpAvatar"> - Scegliendo "Mostra in Ricerca" verrà mostrato: + Scegliendo 'Mostra in Ricerca' verrà mostrato: - il mio profilo nei risultati della ricerca - un link al mio profilo nelle pagine pubbliche del gruppo </notification> @@ -383,7 +386,7 @@ Hai bisogno di un account per entrare in [SECOND_LIFE]. Ne vuoi creare uno adess Compila il tuo annuncio e clicca 'Pubblica...' per aggiungerlo al database. Ti verrà chiesto un prezzo da pagare quando clicchi su Pubblica. Pagare un prezzo più alto fa sì che il tuo annuncio compaia più in alto nella lista, e che sia più facile da trovare quando la gente ricerca per parole chiavi. - <usetemplate ignoretext="Mentre si aggiunge un annuncio" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando si aggiunge una inserzione." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="DeleteClassified"> Cancella annuncio '[NAME]'? @@ -493,14 +496,14 @@ Vuoi visitare [_URL] per maggiori informazioni? <url name="url" option="0"> http://secondlife.com/support/sysreqs.php?lang=it </url> - <usetemplate ignoretext="Mentre sto individuando hardware non supportato" name="okcancelignore" notext="No" yestext="Si"/> + <usetemplate ignoretext="Quando sto individuando hardware non supportato." name="okcancelignore" notext="No" yestext="Si"/> </notification> <notification name="UnknownGPU"> Il tuo sistema contiene una scheda grafica che attualmente non supportiamo. Questo succede spesso con nuovi prodotti che non siamo riusciti a verificare. Probabilmente Second Life funzionerà correttamente ma forse dovrai modificare le impostazioni grafiche in modo appropriato. (Modifica > Preferenze > Grafica). <form name="form"> - <ignore name="ignore" text="Mentre stavo valutando una scheda grafica sconosciuta"/> + <ignore name="ignore" text="Quando sto valutando una scheda grafica sconosciuta"/> </form> </notification> <notification name="DisplaySettingsNoShaders"> @@ -736,7 +739,7 @@ Spiacenti, ma non hai accesso nel luogo di destinazione richiesto. Gli oggetti da te indossati non sono ancoa arrivati. Attendi ancora qualche secondo o scollegati e ricollegati prima di provare a teleportarti. </notification> <notification name="too_many_uploads_tport"> -La gestione dati della regione è al momento occupata e la tua richiesta di teletrasporto non può essere soddisfatta entro breve tempo. Per favore prova di nuovo tra qualche minuto o spostati in un'area meno affollata. +Il server della regione è al momento occupato e la tua richiesta di teletrasporto non può essere soddisfatta entro breve tempo. Per favore prova di nuovo tra qualche minuto o spostati in un'area meno affollata. </notification> <notification name="expired_tport"> Spiacenti, il sistema non riesce a soddisfare la tua richiesta di teletrasporto entro un tempo ragionevole. Riprova tra qualche minuto. @@ -1127,7 +1130,7 @@ Il tuo avatar è stato spostato in una regione vicina. I tuoi vestiti stanno ancora scaricandosi. Puoi usare [SECOND_LIFE] normalmente e gli altri utenti ti vedranno correttamente. <form name="form"> - <ignore name="ignore" text="se gli abiti ci impiegano troppo tempo a scaricarsi"/> + <ignore name="ignore" text="Qualora gli abiti impieghino troppo tempo a caricarsi."/> </form> </notification> <notification name="FirstRun"> @@ -1150,9 +1153,9 @@ Puoi controllare la tua connessione internet e riprovare fra qualche minuto, opp <notification name="WelcomeChooseSex"> Il tuo avatar apparirà fra un attimo. -Usa le frecce per camminare. -Premi F1 in qualunque momento per aiuto o per apprendere altre cose su [SECOND_LIFE]. -Scegli l'avatar maschile o femminile. Puoi sempre cambiare idea più tardi. +Usa le frecce per muoverti. +Premi F1 in qualunque momento per la guida o per apprendere altre cose di [SECOND_LIFE]. +Scegli un avatar maschile o femminile. Puoi sempre cambiare idea più tardi. <usetemplate name="okcancelbuttons" notext="Femminile" yestext="Maschile"/> </notification> <notification name="NotEnoughCurrency"> @@ -1190,13 +1193,13 @@ Scegli solo un oggetto e riprova. Impossibile impostare le texture della regione: La texture del terreno [TEXTURE_NUM] ha una profondità di bit pari a [TEXTURE_BIT_DEPTH] non corretta. -Sostituisci la texture [TEXTURE_NUM] con una a 24-bit 512x512 o una immagine più piccola e quindi clicca nuovamente su "Applica". +Sostituisci la texture [TEXTURE_NUM] con una a 24-bit 512x512 o una immagine più piccola e quindi clicca nuovamente su 'Applica'. </notification> <notification name="InvalidTerrainSize"> Impossibile impostare le texture di regione: La texture del terreno [TEXTURE_NUM] è troppo grande se a [TEXTURE_SIZE_X]x[TEXTURE_SIZE_Y]. -Sostituisci la texture [TEXTURE_NUM] con una a 24-bit 512x512 oppure con una immagine più piccola e quindi clicca di nuovo "Applica". +Sostituisci la texture [TEXTURE_NUM] con una a 24-bit 512x512 oppure con una immagine più piccola e quindi clicca di nuovo 'Applica'. </notification> <notification name="RawUploadStarted"> Importazione iniziata. Può impiegare fino a due minuti, a seconda della velocità della tua connessione. @@ -1285,53 +1288,53 @@ Vuoi avviarne lo scaricamento nella tua cartella applicazioni? <notification name="DeedObjectToGroup"> La cessione di questo oggetto farà in modo che il gruppo: * Riceva i L$ pagati all'oggetto - <usetemplate ignoretext="Quando cedi oggetti ai gruppi" name="okcancelignore" notext="Annulla" yestext="Cedi"/> + <usetemplate ignoretext="Quando cedi oggetti ai gruppi." name="okcancelignore" notext="Annulla" yestext="Cedi"/> </notification> <notification name="WebLaunchExternalTarget"> Apri il tuo browser web per vedere questo contenuto? - <usetemplate ignoretext="Quando apri il browser di sistema per vedere una pagina web" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando apri il browser di sistema per vedere una pagina web." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="WebLaunchJoinNow"> Vuoi andare su www.secondlife.com per gestire il tuo account? - <usetemplate ignoretext="Quando lanci il browser web per gestire il tuo account" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando lanci il browser web per gestire il tuo account." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="WebLaunchBugReport101"> Visita la Wiki di [SECOND_LIFE] per imparare a segnalare un bug correttamente. - <usetemplate ignoretext="Quando lanci il browser web per vedere la Wiki di segnalazione bug base" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere come segnalare bug nella wiki." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="WebLaunchSecurityIssues"> Visita la Wiki di [SECOND_LIFE] per i dettagli su come segnalare un problema di sicurezza. - <usetemplate ignoretext="Quando lanci il browser web per vedere la Wiki sui problemi di sicurezza" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere la Wiki sui problemi di sicurezza." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="WebLaunchQAWiki"> Visita il controllo di qualità Wiki [SECOND_LIFE]. - <usetemplate ignoretext="Quando lanci il browser web per vedere il controllo di qualità Wiki" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere il controllo di qualità Wiki." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="WebLaunchPublicIssue"> Visita il registro pubblico dei problemi di [SECOND_LIFE], dove puoi segnalare bug ed altri problemi. - <usetemplate ignoretext="Quando lanci il browser web per vedere il registro pubblico dei problemi" name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere il registro pubblico dei problemi." name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> </notification> <notification name="WebLaunchPublicIssueHelp"> Visita la Wiki di [SECOND_LIFE] per le informazioni su come usare il registro pubblico dei problemi. - <usetemplate ignoretext="Quando lanci il browser web per vedere la Wiki del registro pubblico dei problemi" name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere la Wiki del registro pubblico dei problemi." name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> </notification> <notification name="WebLaunchSupportWiki"> Vai al blog ufficiale Linden, per le ultime notizie ed informazioni. - <usetemplate ignoretext="Quando lanci il browser web per vedere il blog" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere il blog." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="WebLaunchLSLGuide"> Vai alla guida dello scripting per l'aiuto sullo scripting? - <usetemplate ignoretext="Quando lanci il browser web per vedere la guida dello scripting" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere la guida dello scripting." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="WebLaunchLSLWiki"> Vai al portale LSL per aiuto sullo scripting? - <usetemplate ignoretext="Quando lanci il browser web per vedere il portale LSL" name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> + <usetemplate ignoretext="Quando lanci il browser web per vedere il portale LSL." name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> </notification> <notification name="ReturnToOwner"> Confermi di voler restituire gli oggetti selezionati ai loro proprietari? Gli oggetti trasferibili ceduti al gruppo, verranno restituiti ai proprietari precedenti. *ATTENZIONE* Gli oggetti ceduti non trasferibili verranno cancellati! - <usetemplate ignoretext="Quando restituisci gli oggetti ai loro proprietari" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando restituisci gli oggetti ai loro proprietari." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="GroupLeaveConfirmMember"> Sei attualmente un membro del gruppo [GROUP]. @@ -1369,7 +1372,7 @@ Vuoi cancellare quell'elemento? <notification name="BusyModeSet"> Impostata la modalità 'Occupato'. La chat e i messaggi verranno nascosti. I messaggi IM riceveranno la risposta impostata per la modalità 'Occupato'. Tutte le offerte di teleport verranno declinate. Tutte le offerte di inventario andranno nel cestino. - <usetemplate ignoretext="Quando si imposta la modalità occupato" name="okignore" yestext="OK"/> + <usetemplate ignoretext="Quando imposti la modalità 'occupato'." name="okignore" yestext="OK"/> </notification> <notification name="JoinedTooManyGroupsMember"> Sei membro di troppi gruppi per poterti unire ad uno nuovo. @@ -1442,7 +1445,7 @@ Per abbandonare un gruppo seleziona l'opzione 'Gruppi..' dal menu </notification> <notification name="TeleportFromLandmark"> Confermi di volerti teleportare? - <usetemplate ignoretext="Quando ti teleporti da un landmark nell'inventario" name="okcancelignore" notext="Annulla" yestext="Teleportati"/> + <usetemplate ignoretext="Quando ti teleporti da un landmark dell'inventario." name="okcancelignore" notext="Annulla" yestext="Teleportati"/> </notification> <notification label="Manda un messaggio a tutti nella tua proprietà" name="MessageEstate"> Scrivi un annuncio breve che verrà mandato a tutti quelli che sono in questo momento nella tua proprietà. @@ -1526,7 +1529,7 @@ Vuoi andare alla Knowledge Base per ulteriori informazioni sulle categorie di ac name="okcancelignore" yestext="Vai alla Knowledge Base" notext="Chiudi" - ignoretext="Quando l'entrata nella regione è bloccata a causa delle categorie di accesso"/> + ignoretext="Quando l'entrata nella regione è bloccata a causa delle categorie di accesso."/> </notification> <notification name="RegionEntryAccessBlocked_Notify"> Non sei ammesso in questa regione a causa della tua categoria d'accesso. @@ -1543,7 +1546,7 @@ Puoi cliccare su 'Cambia le preferenze' per aumentare subito le tue pr default="true" name="Cancel" text="Chiudi"/> - <ignore name="ignore" text="Quando l'entrata nella regione è bloccata a causa delle preferenze sulle categorie di accesso"/> + <ignore name="ignore" text="Quando l'entrata in una regione è bloccata a causa delle preferenze delle categorie di accesso."/> </form> </notification> <notification name="LandClaimAccessBlocked"> @@ -1565,7 +1568,7 @@ Vuoi andare alla Knowledge Base per maggiori informazioni sulle categorie di acc name="okcancelignore" yestext="Vai alla Knowledge Base" notext="Chiudi" - ignoretext="Quando la presa di possesso della terra è bloccata a causa delle categorie di accesso"/> + ignoretext="Quando l'acquisizione della terra è bloccata a causa delle categorie di accesso."/> </notification> <notification name="LandClaimAccessBlocked_Notify"> Non puoi prendere possesso di questa terra a causa della tua categoria di accesso. @@ -1578,7 +1581,7 @@ Puoi cliccare su 'Cambia le preferenze' per aumentare subito le tue pr name="okcancelignore" yestext="Cambia le preferenze" notext="Chiudi" - ignoretext="Quando la presa di possesso della terra è bloccata a causa delle preferenze sulle categorie di accesso"/> + ignoretext="Quando l'acquisizione della terra è bloccata a causa delle preferenze delle categorie di accesso."/> </notification> <notification name="LandBuyAccessBlocked"> Non puoi acquistare questo terreno a causa della tua categoria di accesso. Questo può essere dovuto ad una mancanza di informazioni valide che confermino la tua età. @@ -1599,7 +1602,7 @@ Vuoi andare alla Knowledge Base per maggiori informazioni sulle categorie di acc name="okcancelignore" yestext="Vai alla Knowledge Base" notext="Chiudi" - ignoretext="Quando un acquisto di terra è bloccato a causa delle categorie di accesso"/> + ignoretext="Quando un acquisto di terra è bloccato a causa delle categorie di accesso."/> </notification> <notification name="LandBuyAccessBlocked_Notify"> Non puoi acquistare questa land a causa della tua categoria di accesso. @@ -1612,7 +1615,7 @@ Puoi cliccare su 'Cambia le preferenze' per aumentare subito le tue pr name="okcancelignore" yestext="Cambia le preferenze" notext="Chiudi" - ignoretext="Quando un acquisto di terra è bloccato a causa delle preferenze sulle categorie di accesso"/> + ignoretext="Quando un acquisto di terra è bloccato a causa delle preferenze sulle categorie di accesso."/> </notification> <notification name="TooManyPrimsSelected"> "Hai selezionato troppi prims. Seleziona [MAX_PRIM_COUNT] o meno prims e riprova." @@ -1757,9 +1760,9 @@ Se questa opzione è selezionato i compratori possono rivendere i loro terreni i Impostazione base: Non consentire </notification> <notification label="Disabilita gli script" name="HelpRegionDisableScripts"> - Se le prestazioni di una sim sono basse, probabilmente è colpa di uno script. Apri la 'barra delle statistiche' (Ctrl-Shift-1). Controlla il FPS della fisica del simulatore. -Se è più basso di 45, apri il pannello "Tempi" collocato ln fondo alla 'barra delle statistiche' -Se il tempo per gli script è di 25 ms o più alto, clicca sul bottone 'individua script pesanti'. Ti verrà dato il nome e l'ubicazione degli script che probabilmente causano una cattiva prestazione. + Se le prestazioni di una sim sono basse, probabilmente è colpa di uno script. Apri la 'Barra delle statistiche' (Ctrl-Shift-1). Controlla il FPS della fisica del simulatore. +Se è più basso di 45, apri il pannello 'Time' (Tempi) collocato ln fondo alla 'Barra delle statistiche' +Se il tempo per gli script è di 25 ms o più alto, clicca sul bottone 'Visualizza l'elenco degli script più pesanti...'. Ti verrà dato il nome e l'ubicazione degli script che probabilmente causano una cattiva prestazione. Selezionare la casella della disabilitazione script e, premendo il bottone applica, disabilitare temporaneamente tutti gli script in questa regione. Questo è necessario per poterti recare verso l'ubicazione dello script definito nell'elenco come 'script più pesante'. Una volta arrivato all'oggetto, studia lo script per capire se è quello che sta causando il problema. Potresti dover contattare il proprietario dello script oppure cancellare o restituire l'oggetto. Disabilita la casella e quindi premere applica per riattivare gli script nella regione. @@ -1768,10 +1771,10 @@ Impostazione base: spento </notification> <notification label="Disabilita le collisioni" name="HelpRegionDisableCollisions"> Quando le prestazioni della sim sono basse, può darsi che la colpa sia di oggetti fisici. -Apri la 'barra delle statistiche' (Ctrl-Shift-1). +Apri la 'Barra delle statistiche' (Ctrl-Shift-1). Controlla il FPS della fisica del simulatore. -Se è più basso di 45, apri il pannello "Tempi" collocato in fondo alla 'barra delle statistiche'. -Se il tempo della sim (fisica) risulta 20 ms o più, clicca sul bottone "mostra gli oggetti che collidono di più". +Se è più basso di 45, apri il pannello 'Time' (Tempi) collocato in fondo alla 'Barra delle statistiche'. +Se il tempo della sim (fisica) risulta 20 ms o più, clicca sul bottone 'mostra gli oggetti che collidono di più'. Ti verranno dati il nome e l'ubicazione degli oggetti fisici che possono causare una cattiva prestazione. Selezionare la casella disabilita collisioni e, premendo il bottone applica, disabilitare temporaneamente le collisioni oggetto-oggetto. Questo è necessario per poterti recare verso l'ubicazione dell'oggetto che sta collidendo eccessivamente. @@ -1791,11 +1794,11 @@ Impostazione base: spento </notification> <notification label="Oggetti con maggiori collisioni" name="HelpRegionTopColliders"> Mostra una lista di oggetti che sperimentano la maggior quantità di potenziali collisioni oggetto-oggetto. Questi oggetti possono abbassare le prestazioni. -Seleziona Vista > Barra Statistiche e guarda sotto Simulatore > Tempi > Tempo Sim (fisica) per controllare se un tempo di più di 20 ms è impiegato nella fisica. +Seleziona Visualizza > Barra della statistiche e guarda sotto Simulator (Simulatore) > Time (Tempi) > Physics Time (Tempo Sim fisica) per controllare se un tempo di più di 20 ms è impiegato nella fisica. </notification> <notification label="Script pesanti" name="HelpRegionTopScripts"> Mostra una lista degli oggetti che occupano la maggior parte del loro tempo eseguendo script LSL. Questi oggetti possono abbassare le prestazioni. -Seleziona Vista > Barra statistiche e guarda sotto Simulatore > Tempi > Tempo Script per controllare se un tempo di più di 20 ms è impiegato per gli script. +Seleziona Visualizza > Barra della statistiche e guarda sotto Simulator (Simulatore) > Time (Tempi) > Script Time (Tempo Script) per controllare se un tempo di più di 20 ms è impiegato per gli script. </notification> <notification label="Fai ripartire la regione" name="HelpRegionRestart"> Fai ripartire i processi del server che gestisce questa regione dopo un avvertimento di due minuti. Tutti i residenti nella regione verranno scollegati. @@ -1898,7 +1901,7 @@ Impostazione base: spento Un regolamento può essere usato per comunicare regole, linee guida, informazioni culturali o semplicemente ciò che ti aspetti dal possibile compratore. Questo può includere impostazioni in zone, regolamenti architettonici, opzioni di pagamento o qualunque altra informazione che ritieni importante che il nuovo proprietario debba aver visto e accettato prima dell'acquisto. -Il compratore deve accettare il regolamento selezionando la casella appropriata per poter completare l'acquisto. I regolamenti delle proprietà sono sempre visibili nella finestra "Informazioni sul terreno" in tutti gli appezzamenti in cui è stato impostato. +Il compratore deve accettare il regolamento selezionando la casella appropriata per poter completare l'acquisto. I regolamenti delle proprietà sono sempre visibili nella finestra 'Informazioni sul terreno' in tutti gli appezzamenti in cui è stato impostato. </notification> <notification label="Impossibile comprare oggetti" name="BuyObjectOneOwner"> Impossibile comprare oggetti da proprietari diversi nello stesso momento. @@ -1959,7 +1962,7 @@ Il contenuto verrà copiato nel tuo inventario. <usetemplate name="okcancelbuttons" notext="Annulla" yestext="OK"/> </notification> <notification name="ConfirmPurchase"> - Questa transazione farà: + Questa transazione ti permetterà di: [ACTION] Confermi di voler procedere all'acquisto? @@ -1987,19 +1990,19 @@ Hai aggiornato l'ubicazione di questo preferito ma gli altri dettagli conse Questi elementi verranno trasferiti nel tuo inventario, ma non copiati. Trasferisci gli elementi nell'inventario? - <usetemplate ignoretext="Quando si trasferiscono, dagli oggetti all'inventario, elementi non copiabili" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando si trasferiscono elementi non copiabili, dagli oggetti all'inventario." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="MoveInventoryFromScriptedObject"> Hai selezionato elementi dell'inventario non copiabili. Questi elementi verranno trasferiti nel tuo inventario, non verranno copiati. Dato che questo oggetto è scriptato, il trasferimento di questi elementi nel tuo inventario potrebbe causare un malfunzionamento degli script. Trasferisci gli elementi nell'inventario? - <usetemplate ignoretext="Quando si trasferiscono oggetti scriptati non copiabili nell'inventario" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando si trasferiscono oggetti scriptati non copiabili nell'inventario." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="ClickActionNotPayable"> Attenzione: L'azione di pagamento automatico al click è stata impostata, ma funzionerà solo se aggiungi uno script con un evento money(). <form name="form"> - <ignore name="ignore" text="Quando imposti il "Pagamento" di oggetti senza l'evento money()"/> + <ignore name="ignore" text="Quando imposti il 'Pagamento' di oggetti senza l'evento money()"/> </form> </notification> <notification name="OpenObjectCannotCopy"> @@ -2007,7 +2010,7 @@ Trasferisci gli elementi nell'inventario? </notification> <notification name="WebLaunchAccountHistory"> Vai nel sito web di Second Life per vedere il tuo estratto conto? - <usetemplate ignoretext="Quando carichi la pagina web dell'estratto conto" name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> + <usetemplate ignoretext="Quando carichi la pagina web dell'estratto conto." name="okcancelignore" notext="Annulla" yestext="Vai alla pagina"/> </notification> <notification name="ClickOpenF1Help"> Visita il sito di supporto di Second Life? @@ -2104,7 +2107,7 @@ La Linden Lab C'è già un oggetto indossato in questo punto del corpo. Vuoi sostituirlo con l'oggetto selezionato? <form name="form"> - <ignore name="ignore" save_option="true" text="Quando avviene la sostituzione di un oggetto indossato già esistente"/> + <ignore name="ignore" save_option="true" text="Quando avviene la sostituzione di un oggetto indossato già esistente."/> <button name="Yes" text="OK"/> <button name="No" text="Annulla"/> </form> @@ -2114,14 +2117,14 @@ Vuoi sostituirlo con l'oggetto selezionato? Desideri abbandonare la modalità 'Occupato' prima di completare questa transazione? <form name="form"> - <ignore name="ignore" save_option="true" text="Quando avviene il pagamento di una persona o oggetto in modalità 'Occupato'"/> + <ignore name="ignore" save_option="true" text="Quando avviene il pagamento di una persona o di un oggetto in modalità 'Occupato'."/> <button name="Yes" text="OK"/> <button name="No" text="Abbandona"/> </form> </notification> <notification name="ConfirmEmptyTrash"> Confermi di volere permanentemente rimuovere il contenuto del tuo cartella Cestino? - <usetemplate ignoretext="Quando svuoti la cartella cestino del tuo inventario" name="okcancelignore" notext="Annulla" yestext="OK"/> + <usetemplate ignoretext="Quando svuoti la cartella cestino del tuo inventario." name="okcancelignore" notext="Annulla" yestext="OK"/> </notification> <notification name="ConfirmClearBrowserCache"> Confermi di voler pulire la cache del tuo browser? @@ -2137,15 +2140,15 @@ Desideri abbandonare la modalità 'Occupato' prima di completare quest </notification> <notification name="ConfirmEmptyLostAndFound"> Confermi di volere rimuovere permanentemente il contenuto della tua cartalla Persi e ritrovati? - <usetemplate ignoretext="Quando cancelli la cartella persi e ritrovati del tuo inventario" name="okcancelignore" notext="No" yestext="Si"/> + <usetemplate ignoretext="Quando cancelli la cartella degli oggetti perduti e ritrovati del tuo inventario." name="okcancelignore" notext="No" yestext="Si"/> </notification> <notification name="CopySLURL"> - Lo SLURL seguente è stato copiato nei tuoi appunti: + Lo SLurl seguente è stato copiato nei tuoi appunti: [SLURL] Lo puoi inserire in una pagina web per dare ad altri modo di accedere a questo posto o puoi provare a copiarla nella barra indirizzi del tuo browser web. <form name="form"> - <ignore name="ignore" text="Quando copi lo SLURL negli appunti"/> + <ignore name="ignore" text="Quando copi uno SLurl negli appunti."/> </form> </notification> <notification name="GraphicsPreferencesHelp"> @@ -2196,30 +2199,30 @@ Dettaglio del terreno: Imposta la quantità di dettagli che vuoi vedere per le t <notification name="EnvSettingsHelpButton"> Queste impostazioni modificano il modo in cui l'ambiente viene visto localmente sul tuo computer. La tua scheda grafica deve supportare gli effetti atmosferici per poter accedere a tutte le impostazioni. -Modifica il cursore "Ora del Giorno " per cambiare la fase giornaliera locale sul client. +Modifica il cursore 'Ora del Giorno' per cambiare la fase giornaliera locale sul client. -Modifica il cursore "Intensità delle nuvole" per controllare la quantità di nuvole che coprono il cielo. +Modifica il cursore 'Intensità delle nuvole' per controllare la quantità di nuvole che coprono il cielo. -Scegli un colore nella tavolozza colori per il "colore dell'acqua" per cambiare il colore dell'acqua. +Scegli un colore nella tavolozza colori per il 'colore dell'acqua' per cambiare il colore dell'acqua. -Modifica il cursore "Nebbiosità dell'acqua" per controllare quanto densa è la nebbia sott'acqua. +Modifica il cursore 'Nebbiosità dell'acqua' per controllare quanto densa è la nebbia sott'acqua. -Clicca "Usa l'ora della proprietà" per sincronizzare il tempo del giorno con l'ora del giorno della regione e collegarle stabilmente. +Clicca 'Usa l'ora della proprietà' per sincronizzare il tempo del giorno con l'ora del giorno della regione e collegarle stabilmente. -Clicca "Cielo avanzato" per visualizzare un editor con le impostazioni avanzate per il cielo. +Clicca 'Cielo avanzato' per visualizzare un editor con le impostazioni avanzate per il cielo. -Clicca "Acqua Avanzata" per visualizzare un editor con le impostazini avanzate per l'acqua. +Clicca 'Acqua Avanzata' per visualizzare un editor con le impostazini avanzate per l'acqua. </notification> <notification name="HelpDayCycle"> L'editor del Ciclo Giorno/Notte permette di controllare il cielo durante il ciclo giornaliero di Second Life. Questo è il ciclo che è usato dal cursore dell'editor base dell'ambiente. -L'editor del ciclo giorno/notte funziona impostando i fotogrammi chiave. Questi sono nodi (rappresentati da tacche grige sul grafico temporale) che hanno delle preregolazioni associate del cielo. Man mano che l'ora del giorno procede, il cielo di WindLight"si anima" interpolando i valori fra questi fotogrammi chiave. +L'editor del ciclo giorno/notte funziona impostando i fotogrammi chiave. Questi sono nodi (rappresentati da tacche grige sul grafico temporale) che hanno delle preregolazioni associate del cielo. Man mano che l'ora del giorno procede, il cielo di WindLight'si anima' interpolando i valori fra questi fotogrammi chiave. La freccia gialla sopra la linea del tempo rappresenta la tua vista corrente, basata sull'ora del giorno. Cliccandola e spostandola vedrai come il giorno si animerebbe. Si può aggiungere o cancellare fotogrammi chiave cliccando sui tasti 'Aggiungi Fotogramma Chiave' e 'Cancella Fotogramma Chiave' alla destra della linea del tempo. Si possono impostare le posizioni temporali dei fotogrammi chiave spostandole lungo la linea del tempo o impostando il loro valore a mano nella finestra di impostazione dei fotogrammi chiave. All'interno della finestra di impostazione si può associare il fotogramma chiave con le impostazioni corrispondenti di Wind Light. -La durata del ciclo definisce la durata complessiva di un "giorno". Impostando questo ad un valore basso (per esempio, 2 minuti) tutto il ciclo di 24 ore verrà completato in solo 2 minuti reali! Una volta soddisfatto dell tua linea del tempo e le impostazioni dei fotogrammi chiave, usa i bottoni Play e Stop per vederne in anteprima i risultati. Attenzione: si può sempre spostare la freccia gialla indicatrice del tempo sopra la linea del tempo per vedere il ciclo animarsi interattivamente. Scegliendo invece il pulsanto 'Usa il tempo della regione' ci si sincronizza con il le durate del ciclo definite per questa regione. +La durata del ciclo definisce la durata complessiva di un 'giorno'. Impostando questo ad un valore basso (per esempio, 2 minuti) tutto il ciclo di 24 ore verrà completato in solo 2 minuti reali! Una volta soddisfatto dell tua linea del tempo e le impostazioni dei fotogrammi chiave, usa i bottoni Play e Stop per vederne in anteprima i risultati. Attenzione: si può sempre spostare la freccia gialla indicatrice del tempo sopra la linea del tempo per vedere il ciclo animarsi interattivamente. Scegliendo invece il pulsanto 'Usa il tempo della regione' ci si sincronizza con il le durate del ciclo definite per questa regione. Una volta soddisfatto del ciclo giornaliero, puoi salvarlo o ricaricarlo con i bottoni 'Salva test del giorno' e 'Carica il test del giorno'. Attualmente è possibile definire un solo ciclo giorno/notte </notification> @@ -2239,7 +2242,7 @@ E' utile per simulare scene con un livello alto di fumo e di inquinamento d E' anche utile per simulare la nebbia e la foschia al mattino. </notification> <notification name="HelpDensityMult"> - Il moltiplicatore di densità può essere usato per influenzare la densità atmosferica generale. Con valori bassi, crea la sensazione di "aria sottile", con valori alti crea un effetto molto pesante, annebbiato. + Il moltiplicatore di densità può essere usato per influenzare la densità atmosferica generale. Con valori bassi, crea la sensazione di 'aria sottile', con valori alti crea un effetto molto pesante, annebbiato. </notification> <notification name="HelpDistanceMult"> Modifica la distanza percepita da WindLight. @@ -2248,7 +2251,7 @@ Valori più grandi di 1 simulano distanze più grandi per effetti atmosferici pi </notification> <notification name="HelpMaxAltitude"> Altitudine Massima modifica i calcoli di altezza che fa WindLight quando calcola l'illuminazione atmosferica. -In periodi successivi del giorno, è utile per modificare quanto "profondo" appaia il tramonto. +In periodi successivi del giorno, è utile per modificare quanto 'profondo' appaia il tramonto. </notification> <notification name="HelpSunlightColor"> Modifica il colore e l'intensità della luce diretta nella scena. @@ -2257,8 +2260,8 @@ In periodi successivi del giorno, è utile per modificare quanto "profondo& Modifica il colore e l'intensità della luce atmosferica ambientale nella scena. </notification> <notification name="HelpSunGlow"> - Il cursore Dimensione controlla la dimensione del sole. -Lo slider "Focus" controlla quanto è offuscato il sole sopra il cielo. + Il cursore Grandezza controlla la dimensione del sole. +Il cursore Focus controlla quanto è offuscato il sole sopra il cielo. </notification> <notification name="HelpSceneGamma"> Modifica la distribuzione di luci e ombre nello schermo. @@ -2392,7 +2395,7 @@ D (Densità) controlla quanto gonfie o spezzettate appaiono le nuvole. </notification> <notification name="AutoWearNewClothing"> Vuoi indossare automaticamente i vestiti che crei? - <usetemplate ignoretext="Indossa automaticamente i nuovi vestiti." name="okcancelignore" notext="No" yestext="Si"/> + <usetemplate ignoretext="Quando Indossi automaticamente nuovi vestiti." name="okcancelignore" notext="No" yestext="Si"/> </notification> <notification name="NotAgeVerified"> La tua età deve essere verificata per poter entrare in questo territorio. @@ -2402,7 +2405,7 @@ Vuoi visitare il sito di Second Life per verificare la tua eta? <url name="url" option="0"> https://secondlife.com/account/verification.php </url> - <usetemplate ignoretext="Avviso per mancanza della verifica dell'età" name="okcancelignore" notext="No" yestext="Si"/> + <usetemplate ignoretext="Quando hai un avviso per mancanza della verifica dell'età." name="okcancelignore" notext="No" yestext="Si"/> </notification> <notification name="Cannot enter parcel: no payment info on file"> Questo terreno richiede che tu abbia registrato le tue informazioni di pagamento prima che tu possa accedervi. @@ -2412,7 +2415,7 @@ Vuoi visitare il sito di Second Life per impostarle? <url name="url" option="0"> https://secondlife.com/account/index.php?lang=it </url> - <usetemplate ignoretext="Avviso per mancanza di informazioni di pagamento registrato" name="okcancelignore" notext="No" yestext="Si"/> + <usetemplate ignoretext="Quando hai un avviso per mancanza di informazioni di pagamento registrate." name="okcancelignore" notext="No" yestext="Si"/> </notification> <notification name="MissingString"> La stringa [STRING_NAME] non è presente in strings.xml @@ -2673,7 +2676,7 @@ Vai alla 'Help Island Public' per ripetere il tutorial. <notification name="ImproperPaymentStatus"> Non hai una impostazioni di pagamento corrette per entrare in questa regione. </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> Devi avere un'età verificata per entrare in questa regione. </notification> <notification name="MustGetAgeParcel"> @@ -2957,7 +2960,7 @@ Gli oggetti flessibili non possono essere fisici e devano essere fantasma fino a <notification name="FirstDebugMenus"> Hai attivato il menu Avanzato. Questo menu contiene funzioni utili per gli sviluppatori per il debug di Second Life. -Per attivare o disattivare questo menu su Windows premere Ctrl-Alt-D. Su Mac premere Cmd-Opt-Shift-D. +Per attivare o disattivare questo menu su Windows premere Ctrl-Alt-D. Su Mac premere ⌘-Opt-D. </notification> <notification name="FirstSculptedPrim"> Si sta modificando uno sculpted prim. diff --git a/indra/newview/skins/default/xui/ja/floater_world_map.xml b/indra/newview/skins/default/xui/ja/floater_world_map.xml index c6385980ef..f7fe65bb72 100644 --- a/indra/newview/skins/default/xui/ja/floater_world_map.xml +++ b/indra/newview/skins/default/xui/ja/floater_world_map.xml @@ -53,6 +53,6 @@ <button label="目的地を表示" label_selected="目的地を表示" name="Show Destination" tool_tip="選択したロケーションを地図の中心にする"/> <button label="クリア" label_selected="クリア" name="Clear" tool_tip="トラッキングを停止"/> <button label="現在地を表示" label_selected="現在地を表示" name="Show My Location" tool_tip="あなたのアバターのロケーションを地図の中心にする"/> - <button label="SLURLをクリップボードにコピー" name="copy_slurl" tool_tip="現在地をSLURLとしてコピーし、ウェブで使用"/> + <button label="SLurlをクリップボードにコピー" name="copy_slurl" tool_tip="現在地をSLurlとしてコピーし、ウェブで使用"/> <slider label="ズーム" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/ja/notifications.xml b/indra/newview/skins/default/xui/ja/notifications.xml index c20a4f50b4..478bcfda18 100644 --- a/indra/newview/skins/default/xui/ja/notifications.xml +++ b/indra/newview/skins/default/xui/ja/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> 常にこのオプションを選択 </global> + <global name="implicitclosebutton"> + 閉じる + </global> <template name="okbutton"> <form> <button @@ -2299,13 +2302,13 @@ Linden Lab <usetemplate ignoretext="持ち物内の「遺失物」フォルダを空にするとき" name="okcancelignore" notext="いいえ" yestext="はい"/> </notification> <notification name="CopySLURL"> - 以下のSLURLがクリップボードにコピーされました。 + 以下のSLurlがクリップボードにコピーされました。 [SLURL] 他の人がアクセスしやすいようにウェブ・ページに載せたり、 ブラウザのアドレス・バーに貼り付けて、自分でアクセスしてみましょう。 <form name="form"> - <ignore name="ignore" text="SLURLをクリップボードにコピーするとき"/> + <ignore name="ignore" text="SLurlをクリップボードにコピーするとき"/> </form> </notification> <notification name="GraphicsPreferencesHelp"> @@ -2869,7 +2872,7 @@ Second Life のウェブサイトにアクセスして、設定しますか? <notification name="ImproperPaymentStatus"> この地域(リージョン)に入るために適した支払いステータスがありません。 </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> この地域(リージョン)に入るには年齢確認済みである必要があります。 </notification> <notification name="MustGetAgeParcel"> @@ -3165,7 +3168,7 @@ Second Life をデバッグするデベロッパーにとって 有用な機能があります。 このメニューを切り替えるには、 WindowsではCtrl-Alt-Dを押します。 -Macの場合は、Cmd-Opt-Shift-Dを押してください。 +Macの場合は、⌘-Opt-Shift-Dを押してください。 </notification> <notification name="FirstSculptedPrim"> スカルプトプリムを編集しています。 @@ -3200,7 +3203,7 @@ Macの場合は、Cmd-Opt-Shift-Dを押してください。 [FIRST] [LAST]に持ち物を渡したため、 無視設定が自動的に解除されました。 </notification> <notification name="VoiceInviteGroup"> - [NAME]が、グループ[GROUP]とのボイスチャットコールに参加しました。 + [NAME]が、 グループ[GROUP]とのボイスチャットコールに参加しました。 コールに参加するには「受け入れる」をクリックし、招待を断るときは「拒否」をクリックしてください。このコールをしている人をミュートにする場合は「ミュート」をクリックしてください。 <form name="form"> <button name="Accept" text="受け入れる"/> @@ -3209,7 +3212,7 @@ Macの場合は、Cmd-Opt-Shift-Dを押してください。 </form> </notification> <notification name="VoiceInviteAdHoc"> - [NAME]が、会議チャットでボイスチャットコールに参加しました。 + [NAME]が、 会議チャットでボイスチャットコールに参加しました。 コールに参加するには「受け入れる」をクリックし、招待を断るときは「拒否」をクリックしてください。 このユーザーをミュート(消声)する場合は「ミュート」をクリックしてください。 <form name="form"> <button name="Accept" text="受け入れる"/> @@ -3218,7 +3221,7 @@ Macの場合は、Cmd-Opt-Shift-Dを押してください。 </form> </notification> <notification name="InviteAdHoc"> - [NAME]が、あなたを会議チャットに招待しています。 + [NAME]が、 あなたを会議チャットに招待しています。 チャットに参加するには「受け入れる」をクリックし、招待を断るときは「拒否」をクリックしてください。このユーザーをミュート(消声)する場合は「ミュート」をクリックしてください。 <form name="form"> <button name="Accept" text="受け入れる"/> diff --git a/indra/newview/skins/default/xui/nl/floater_world_map.xml b/indra/newview/skins/default/xui/nl/floater_world_map.xml index 1205884062..4385442cd3 100644 --- a/indra/newview/skins/default/xui/nl/floater_world_map.xml +++ b/indra/newview/skins/default/xui/nl/floater_world_map.xml @@ -49,6 +49,6 @@ <button label="Toon bestemming" label_selected="Toon bestemming" name="Show Destination" tool_tip="Centreer kaart op geselecteerde locatie"/> <button label="Verwijder" label_selected="Verwijder" name="Clear" tool_tip="Stop volgen"/> <button label="Toon mijn locatie" label_selected="Toon mijn locatie" name="Show My Location" tool_tip="Centreer kaart op de locatie van uw avatar"/> - <button label="Kopieer SLURL naar klembord" name="copy_slurl" tool_tip="Kopieert huidige locatie als SLURL, zodat deze op het web gebruikt kan worden."/> + <button label="Kopieer SLurl naar klembord" name="copy_slurl" tool_tip="Kopieert huidige locatie als SLurl, zodat deze op het web gebruikt kan worden."/> <slider label="Zoom" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/nl/menu_inventory.xml b/indra/newview/skins/default/xui/nl/menu_inventory.xml index f406bcf6bc..6a514422b9 100644 --- a/indra/newview/skins/default/xui/nl/menu_inventory.xml +++ b/indra/newview/skins/default/xui/nl/menu_inventory.xml @@ -12,7 +12,7 @@ <menu_item_call label="Nieuw script" name="New Script"/> <menu_item_call label="Nieuwe notitie" name="New Note"/> <menu_item_call label="Nieuw gebaar" name="New Gesture"/> - <menu name="New Clothes"> + <menu name="New Clothes" label="Nieuwe Kleding"> <menu_item_call label="Nieuw shirt" name="New Shirt"/> <menu_item_call label="Nieuwe broek" name="New Pants"/> <menu_item_call label="Nieuwe schoenen" name="New Shoes"/> @@ -23,7 +23,7 @@ <menu_item_call label="Nieuw onderhemd" name="New Undershirt"/> <menu_item_call label="Nieuwe onderbroek" name="New Underpants"/> </menu> - <menu name="New Body Parts"> + <menu name="New Body Parts" label="Nieuwe Lichaamsdelen"> <menu_item_call label="Nieuwe postuur" name="New Shape"/> <menu_item_call label="Nieuwe huid" name="New Skin"/> <menu_item_call label="Nieuw haar" name="New Hair"/> diff --git a/indra/newview/skins/default/xui/nl/notifications.xml b/indra/newview/skins/default/xui/nl/notifications.xml index c64c2cd021..4c64c93de3 100644 --- a/indra/newview/skins/default/xui/nl/notifications.xml +++ b/indra/newview/skins/default/xui/nl/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> Kies altijd deze optie </global> + <global name="implicitclosebutton"> + Sluiten + </global> <template name="okbutton"> <form> <button @@ -245,7 +248,7 @@ Naar de Second Life website gaan voor meer informatie over partner schap? * Klik op Laden > 'Thuis pagina URL' om terug te keren naar het web profiel van deze Inwoner indien U verder genavigeerd bent. Indien u uw eigen profiel bekijkt, kunt U elke willekeurige URL opgeven als uw web profiel en op OK klikken om het in te stellen. -Andere Inwoners kunnen de door U opgegeven URL bezoeken indien zijn uw profiel bekijken. +Andere Inwoners kunnen de door U opgegeven URL bezoeken indien zij uw profiel bekijken. </notification> <notification name="JoinGroupCanAfford"> Deelname aan deze groep kost L$[COST]. @@ -418,10 +421,10 @@ Betaalde advertentiekosten zullen niet worden terug gestort. <usetemplate name="okcancelbuttons" notext="Annuleren" yestext="OK"/> </notification> <notification name="CacheWillClear"> - De Cache zal geleegd worden als u [SECOND_LIFE] opnieuw start. + De cache zal geleegd worden als u [SECOND_LIFE] opnieuw start. </notification> <notification name="CacheWillBeMoved"> - De Cache zal verplaatst worden als u [SECOND_LIFE] opnieuw start. + De cache zal verplaatst worden als u [SECOND_LIFE] opnieuw start. Opmerking: Dit zal de Cache legen. </notification> <notification name="ChangeConnectionPort"> @@ -1541,7 +1544,7 @@ Ga naar de kennisbank voor meer informatie over inhoudscategorieën? U wordt niet in die regio toegelaten vanwege uw inhoudscategorie. U kunt klikken op 'Wijzig voorkeur' om uw inhoudscategorie voorkeur nu te verhogen en toegelaten te worden. U zult in staat zijn om [REGIONMATURITY] inhoud te zoeken en benaderen vanaf dit moment. Wanneer u later deze instelling wilt wijzigen, ga dan naar Bewerken > Voorkeuren... > Algemeen. - <form> + <form name="form"> <button name="OK" text="Wijzig voorkeur"/> @@ -1549,7 +1552,7 @@ U kunt klikken op 'Wijzig voorkeur' om uw inhoudscategorie voorkeur nu default="true" name="Cancel" text="Sluiten"/> - <ignore text="Wanneer regiotoegang wordt geblokkeerd vanwege de inhoudscategorie voorkeur"/> + <ignore name="ignore" text="Wanneer regiotoegang wordt geblokkeerd vanwege de inhoudscategorie voorkeur"/> </form> </notification> <notification name="LandClaimAccessBlocked"> @@ -2137,7 +2140,7 @@ Wilt u de Niet Storen Modus verlaten voordat u deze transactie completeert? <usetemplate ignoretext="Bij verwijderen van de inhoud uit de inventaris vuilnisbak map" name="okcancelignore" notext="Annuleren" yestext="OK"/> </notification> <notification name="ConfirmClearBrowserCache"> - Weet u zeker dat u uw browser cache wilt legen? + Weet u zeker dat u uw browsercache wilt legen? <usetemplate name="okcancelbuttons" notext="Annuleren" yestext="Ja"/> </notification> <notification name="ConfirmClearCookies"> @@ -2153,34 +2156,34 @@ Wilt u de Niet Storen Modus verlaten voordat u deze transactie completeert? <usetemplate ignoretext="Bij legen van uw inventaris Verloren en Gevonden map" name="okcancelignore" notext="Nee" yestext="Ja"/> </notification> <notification name="CopySLURL"> - De volgende SLURL is gekopieerd naar uw klem bord: + De volgende SLurl is gekopieerd naar uw klem bord: [SLURL] Plaats het in een web pagina om anderen eenvoudig toegang te verschaffen naar de locatie of test het zelf door het te plakken in de adres balk van uw web browser. <form name="form"> - <ignore name="ignore" text="Bij kopiëren van een SLURL naar het klem bord"/> + <ignore name="ignore" text="Bij kopiëren van een SLurl naar het klem bord"/> </form> </notification> <notification name="GraphicsPreferencesHelp"> - Dit venster bepaald de venster afmetingen, resolutie en kwaliteit van de client's grafische weergave. De Voorkeuren > Grafische interface laat u kiezen uit vier grafische niveaus: Laag, Middel, Hoog en Ultra. U kunt ook uw grafische instellingen aanpassen met het aan vinken van het Custom vakje en de volgende instellingen manipuleren: + Dit venster bepaald de venster afmetingen, resolutie en kwaliteit van de client's grafische weergave. De Voorkeuren > Grafische interface laat u kiezen uit vier grafische niveaus: Laag, Middel, Hoog en Ultra. U kunt ook uw grafische instellingen aanpassen met het aan vinken van het Aangepast vakje en de volgende instellingen manipuleren: Shaders: In of uitschakelen van de verschillende typen pixel shaders. -Reflectie Detail: Stelt het type objecten in hetgeen water kan reflecteren. +Reflectiedetail: Stelt het type objecten in hetgeen water kan reflecteren. -Avatar Weergave: Stelt de opties in die van invloed zijn op hoe de client een avatar zal renderen. +Avatarweergave: Stelt de opties in die van invloed zijn op hoe de client een avatar zal renderen. -Zicht bereik: Beïnvloed tot hoe ver objecten vanaf uw zichtpunt worden weergegeven in de scène. +Zichtbereik: Beïnvloed tot hoe ver objecten vanaf uw zichtpunt worden weergegeven in de scène. Maximaal Aantal Particles: Stelt het maximaal aantal particles in die u tegelijk kunt zien op uw scherm. -Nabewerking Kwaliteit: Stelt de resolutie in waarmee Gloei wordt weergegeven. +Nabewerkingskwaliteit: Stelt de resolutie in waarmee Gloei wordt weergegeven. -Maas Detail: Stelt de hoeveelheid detail of het aantal driehoeken in gebruikt voor de weergave van bepaalde objecten. Een hogere waarde zal langer nemen om weer te gegeven, maar zorgen voor objecten met meer detail. +Maasdetail: Stelt de hoeveelheid detail of het aantal driehoeken in gebruikt voor de weergave van bepaalde objecten. Een hogere waarde zal langer nemen om weer te gegeven, maar zorgen voor objecten met meer detail. -Licht Detail: Bepaald welke typen lichten u wenst weer te geven. +Lichtdetail: Bepaald welke typen lichten u wenst weer te geven. -Terrein Detail: Stelt de hoeveelheid detail in die u wilt zien voor het terrein textuur. +Terreindetail: Stelt de hoeveelheid detail in die u wilt zien voor het terrein textuur. </notification> <notification name="WLSavePresetAlert"> Wilt u de opgeslagen voor instellingen overschrijven? @@ -2671,7 +2674,7 @@ Gaat u alstublieft naar de kennisbank voor details over het betreden van gebiede <notification name="ImproperPaymentStatus"> U heeft niet de juiste betalingstatus om deze regio binnen te gaan. </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> U moet leeftijd geverifieerd zijn om deze regio binnen te gaan. </notification> <notification name="MustGetAgeParcel"> @@ -2953,7 +2956,7 @@ Flexibele objecten mogen niet fysiek zijn en moeten fantoom zijn tot de 'fl <notification name="FirstDebugMenus"> U heeft het menu Geavanceerd geactiveerd. Dit menu bevat opties die handig zijn voor ontwikkelaars tijdens het debuggen van Second Life. -Om dit menu in en uit te schakelen drukt u binnen Windows Ctrl-Alt-D. Met een Mac drukt u Cmd-Opt-Shift-D. +Om dit menu in en uit te schakelen drukt u binnen Windows Ctrl-Alt-D. Met een Mac drukt u ⌘-Opt-D. </notification> <notification name="FirstSculptedPrim"> U bent een sculpted prim aan het bewerken. diff --git a/indra/newview/skins/default/xui/nl/strings.xml b/indra/newview/skins/default/xui/nl/strings.xml index 4b64586887..49e566080a 100644 --- a/indra/newview/skins/default/xui/nl/strings.xml +++ b/indra/newview/skins/default/xui/nl/strings.xml @@ -55,6 +55,9 @@ <string name="LoginDownloadingClothing"> Kleding downloaden... </string> + <string name="Quit"> + Afsluiten + </string> <string name="AgentLostConnection"> Deze regio kan problemen ondervinden. Controleer alstublieft uw verbinding met het internet. </string> diff --git a/indra/newview/skins/default/xui/pl/floater_god_tools.xml b/indra/newview/skins/default/xui/pl/floater_god_tools.xml index c978da6d65..762b9f0f55 100755 --- a/indra/newview/skins/default/xui/pl/floater_god_tools.xml +++ b/indra/newview/skins/default/xui/pl/floater_god_tools.xml @@ -42,7 +42,7 @@ <line_editor left_delta="45" name="redirecty" width="35" /> <spinner name="billable factor"/> <text name="billable factor text"> - Czynnik Płatnośći: + Czynnik Płatności: </text> <spinner name="land cost"/> <text name="land cost text"> @@ -68,8 +68,8 @@ <text name="target_avatar_name"> (brak) </text> - <button label="Usuń cel 's skryptowane obiekty na innych posiadłościach" label_selected="Usuń cel 's skryptowane obiekty na innych posiadłościach" name="Delete Target's Scripted Objects On Others Land" /> - <button label="Usuń cel 's skryptowane obiekty na jakichkolwiek posiadłościach" label_selected="Usuń cel 's skryptowane obiekty na jakichkolwiek posiadłościach" name="Delete Target's Scripted Objects On *Any* Land" /> + <button label="Usuń cel z oskryptowanych obiektów na innych posiadłościach" label_selected="Usuń cel 's skryptowane obiekty na innych posiadłościach" name="Delete Target's Scripted Objects On Others Land" /> + <button label="Usuń cel z oskryptowanych obiektów na jakichkolwiek posiadłościach" label_selected="Usuń cel 's skryptowane obiekty na jakichkolwiek posiadłościach" name="Delete Target's Scripted Objects On *Any* Land" /> <button label="Usuń wszystkie cele i obiekty" label_selected="Usuń wszystkie cele i obiekty" name="Delete *ALL* Of Target's Objects" /> <button label="Główne Kolizje" label_selected="Główne Kolizje" name="Get Top Colliders" /> <button label="Główne Skrypty" label_selected="Główne Skrypty" name="Get Top Scripts" /> diff --git a/indra/newview/skins/default/xui/pl/floater_world_map.xml b/indra/newview/skins/default/xui/pl/floater_world_map.xml index c18c529c94..42befb1aff 100755 --- a/indra/newview/skins/default/xui/pl/floater_world_map.xml +++ b/indra/newview/skins/default/xui/pl/floater_world_map.xml @@ -54,6 +54,6 @@ <button label="Pokaż Cel" label_selected="Pokaż Cel" name="Show Destination" tool_tip="Wycentruj miejsce celu na mapie"/> <button label="Wyczyść" label_selected="Wyczyść" name="Clear" tool_tip="Stop tracking"/> <button label="Pokaż moje miejsce" label_selected="Pokaż moje miejsce" name="Show My Location" tool_tip="Wycentruj pozcję awatara na mapie"/> - <button label="Kopiuj SLURL do schowka" name="copy_slurl" tool_tip="Kopie SLURL obecnego miejsca będę mogły zostać użyte na stronie internetowej."/> + <button label="Kopiuj SLurl do schowka" name="copy_slurl" tool_tip="Kopie SLurl obecnego miejsca będę mogły zostać użyte na stronie internetowej."/> <slider label="Skala" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/pl/notifications.xml b/indra/newview/skins/default/xui/pl/notifications.xml index e2b26bf0d1..84075b9db8 100644 --- a/indra/newview/skins/default/xui/pl/notifications.xml +++ b/indra/newview/skins/default/xui/pl/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> Pozwalaj na wybór tej opcji </global> + <global name="implicitclosebutton"> + Zamknij + </global> <template name="okbutton"> <form> <button @@ -2088,12 +2091,12 @@ Chcesz wyłączyć Tryb Pracy przed zakończeniem tej tranzakcji? <usetemplate ignoretext="Usuwając szafę z Twojego foldera Zgubione i Znalezione" name="okcancelignore" notext="Nie" yestext="Tak"/> </notification> <notification name="CopySLURL"> - Następujący link SLURL został skopiowany do pamięci podręcznej: + Następujący link SLurl został skopiowany do pamięci podręcznej: [SLURL] Zamieść go na stronie Internetowej żeby umożliwić innym łatwy dostęp do tego miejsca, albo wklej go do panela adresu Twojej przeglądarki żeby go wypróbować. <form name="form"> - <ignore name="ignore" text="Kopiując SLURL do pamięci podręcznej"/> + <ignore name="ignore" text="Kopiując SLurl do pamięci podręcznej"/> </form> </notification> <notification name="GraphicsPreferencesHelp"> @@ -2617,7 +2620,7 @@ Odwiedź 'Help Island Public' by powtórzyć szkolenie. <notification name="ImproperPaymentStatus"> Nie posiadasz odpowiedniego statusu płatniczego by uzyskać dostęp do regionu. </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> By odwiedzić ten region, Twoje konto musi zostać poddane weryfikacji wieku. </notification> <notification name="MustGetAgeParcel"> @@ -2718,7 +2721,7 @@ Spróbuj ponowanie za kilka minut. </form> </notification> <notification name="TeleportOffered"> - [NAME] proponuje Ci teleportcję do siebie: + [NAME] proponuje Ci teleportację do siebie: [MESSAGE] <form name="form"> @@ -2899,8 +2902,8 @@ Obiekty elastyczne nie mogą być fizyczne i muszą być typu fantom. </notification> <notification name="FirstDebugMenus"> Zaawansowane menu zostało włączone. -To menu zawiera funkcje użyteczne dla programistów analizujących Secon Life. -To menu jest włączne przez Ctrl-Alt-D pod Windows. Mac używa Cmd-Opt-Shift-D. +To menu zawiera funkcje użyteczne dla programistów analizujących Second Life. +To menu jest aktywowane dzięki kombinacji klawiszy Ctrl-Alt-D (Windows) lub ⌘-Opt-D (Mac). </notification> <notification name="FirstSculptedPrim"> Edytujesz sculpt. diff --git a/indra/newview/skins/default/xui/pl/strings.xml b/indra/newview/skins/default/xui/pl/strings.xml index 5067f044e0..3ec2b45237 100755 --- a/indra/newview/skins/default/xui/pl/strings.xml +++ b/indra/newview/skins/default/xui/pl/strings.xml @@ -55,6 +55,9 @@ <string name="LoginDownloadingClothing"> Ładowanie ubrania... </string> + <string name="Quit"> + Wyjdź + </string> <string name="AgentLostConnection"> Ten region może mieć problemy. Sprawdź podłączenie do Internetu. </string> diff --git a/indra/newview/skins/default/xui/pt/floater_world_map.xml b/indra/newview/skins/default/xui/pt/floater_world_map.xml index 0fb8684077..bc4b5462ef 100644 --- a/indra/newview/skins/default/xui/pt/floater_world_map.xml +++ b/indra/newview/skins/default/xui/pt/floater_world_map.xml @@ -48,6 +48,6 @@ <button font="SansSerifSmall" left_delta="91" width="135" label="Mostrar destino" label_selected="Mostrar Destino" name="Show Destination" tool_tip="Centralizar mapa na posição selecionada"/> <button font="SansSerifSmall" label="Limpar" label_selected="Limpar" name="Clear" tool_tip="Parar de percorrer"/> <button font="SansSerifSmall" left_delta="91" width="135" label="Mostra minha localização" label_selected="Mostra minha localização" name="Show My Location" tool_tip="Centraliza o mapa na posição do seu Avatar"/> - <button font="SansSerifSmall" label="Copiar SLURL para área de transferência" name="copy_slurl" tool_tip="Copia a posição atual como SLURL para ser usada na Web"/> + <button font="SansSerifSmall" label="Copiar SLurl para área de transferência" name="copy_slurl" tool_tip="Copia a posição atual como SLurl para ser usada na Web"/> <slider label="Zoom" name="zoom slider"/> </floater> diff --git a/indra/newview/skins/default/xui/pt/notifications.xml b/indra/newview/skins/default/xui/pt/notifications.xml index a88ab65c7a..cc90b6d08f 100644 --- a/indra/newview/skins/default/xui/pt/notifications.xml +++ b/indra/newview/skins/default/xui/pt/notifications.xml @@ -6,6 +6,9 @@ <global name="alwayschoose"> Sempre escolher esta opção </global> + <global name="implicitclosebutton"> + Fechar + </global> <template name="okbutton"> <form> <button @@ -174,7 +177,7 @@ Você deseja permitir que os residentes selecionados tenham direito de edição? Membros não podem ser removidos dessa função. Os membros podem, eles próprios, recusar a função. Deseja continuar? - <usetemplate ignoretext="Quando adcionar membro ao grupo como dono" name="okcancelignore" notext="Não" yestext="Sim"/> + <usetemplate ignoretext="Quando adicionar membro ao grupo como dono" name="okcancelignore" notext="Não" yestext="Sim"/> </notification> <notification name="AssignDangerousActionWarning"> Você está prestes a adicionar a habilidade '[ACTION_NAME]' para a função '[ROLE_NAME]' @@ -954,7 +957,7 @@ Se o problema persistir, por favor clicar sobre 'Ferramentas > Bug Repor Você foi deslogado do [SECOND_LIFE]: [MESSAGE] Você ainda pode olhar o bate-papo e as mensagens instantâneas existentes, clicando em 'Exibir IM & bate-papo'. Caso contrário, clique em 'Sair' para sair do [SECOND_LIFE] imediatamente. - <usetemplate name="okcancelbuttons" notext="Sair" yestext="Ver Mensagem Instantânea & Bate- Papo"/> + <usetemplate name="okcancelbuttons" notext="Sair" yestext="Exibir IM & bate-papo"/> </notification> <notification name="OnlyOfficerCanBuyLand"> Não é possível comprar o terreno para o grupo: @@ -1100,7 +1103,7 @@ Transferir propriedade destes [AREA] m² de terreno para o grupo '[GROUP_NA Configurações de display foram ajustadas para níveis de segurança porque você especificou -- opção de segurança. </notification> <notification name="DisplaySetToRecommended"> - Configurações de display foram ajustadas para nível recomendado basedo na configuração do seu sistema. + Configurações de display foram ajustadas para nível recomendado baseado na configuração do seu sistema. </notification> <notification name="ErrorMessage"> [ERROR_MESSAGE] @@ -1517,7 +1520,7 @@ Ir para o Banco de Conhecimento para maiores informações sobre Classificaçõe Você não é permitido nessa região devido à sua preferência de Classificação de maturidade. Você pode clicar em 'Mudar Preferência' para aumentar sua preferência de Classificação de maturidade agora e permitir sua entrada. Você estará habilitado a buscar e acessar conteúdo [REGIONMATURITY] a partir de agora. Se você desejar mais tarde voltar à configuração anterior, vá para Editar > Preferencias... > Geral. - <form> + <form name="form"> <button name="OK" text="Mudar Preferência"/> @@ -1525,7 +1528,7 @@ Você pode clicar em 'Mudar Preferência' para aumentar sua preferên default="true" name="Cancel" text="Fechar"/> - <ignore text="Quando a entrada na Região está bloqueada devido à preferência de Classificação de maturidade"/> + <ignore name="ignore" text="Quando a entrada na Região está bloqueada devido à preferência de Classificação de maturidade"/> </form> </notification> <notification name="LandClaimAccessBlocked"> @@ -2109,7 +2112,7 @@ Você gostaria de deixar o modo Ocupado antes de completar esta transação? <usetemplate ignoretext="Ao esvaziar pasta Achados e Perdidos do seu inventário" name="okcancelignore" notext="Não" yestext="Sim"/> </notification> <notification name="CopySLURL"> - A seguinte SLURL foi copiada para o seu clipboard: + A seguinte SLurl foi copiada para o seu clipboard: [SLURL] Coloque-a em uma página web para dar aos outros um fácil acesso a este local ou tente você, colando-a na barra de endereços do seu navegador. @@ -2118,9 +2121,9 @@ Coloque-a em uma página web para dar aos outros um fácil acesso a este local o </form> </notification> <notification name="GraphicsPreferencesHelp"> - Este painel controla o tamanho da janela, resolução e a qualidade dos gráficos do computador cliente. O Preferências > Interface Gráfica permite escolher entre quatro níveis gráficos: Baixo, Médio, Alto e Ultra. Você também pode personalizar suas configurações de gráficos selecionando a opção Custom e manipulando as seguintes definições: + Este painel controla o tamanho da janela, resolução e a qualidade dos gráficos do computador cliente. O Preferências > Interface Gráfica permite escolher entre quatro níveis gráficos: Baixo, Médio, Alto e Ultra. Você também pode personalizar suas configurações de gráficos selecionando a opção Personalizar e manipulando as seguintes definições: -Sombreamento: Ativar ou desativar vários tipos de sobreadores de pixel. +Sombreamento: Ativar ou desativar vários tipos de sombreadores de pixel. Detalhes de Reflexão: Define os tipos de objetos que a água pode refletir. @@ -2360,7 +2363,7 @@ Gostaria de visitar o site do Second Life para verificação de idade? [_URL] <url name="url" option="0"> - https://secondlife.com/account/verification.php + https://secondlife.com/account/verification.php?lang=pt </url> <usetemplate ignoretext="Alertar sobre a falta de verificação de idade" name="okcancelignore" notext="Não" yestext="Sim"/> </notification> @@ -2370,7 +2373,7 @@ Gostaria de visitar o site do Second Life para configurá-lo? [_URL] <url name="url" option="0"> - https://secondlife.com/account/ + https://secondlife.com/account/index.php?lang=pt </url> <usetemplate ignoretext="Avisar sobre a falta de informação de pagamento." name="okcancelignore" notext="Não" yestext="Sim"/> </notification> @@ -2627,7 +2630,7 @@ Vá para a Ilha de Ajuda Pública para repetir este tutorial. <notification name="ImproperPaymentStatus"> Você não tem o status de pagamento adequado para entrar nesta região. </notification> - <notification name="MustGetAgeRgion"> + <notification name="MustGetAgeRegion"> Você precisa ter sua idade verificada para entrar nesta região. </notification> <notification name="MustGetAgeParcel"> @@ -2910,7 +2913,7 @@ Objetos flexíveis não podem ser físicos e devem ser fantasmas até que a caix <notification name="FirstDebugMenus"> Você ativou o menu Avançado. Este menu contém funcionalidades úteis para desenvolvedores debugarem o Second Life. -Para mostrar esse menu no Windows pressione Ctrl-Alt-D. No Mac pressione Cmd-Opt-Shift-D. +Para mostrar esse menu no Windows pressione Ctrl-Alt-D. No Mac pressione ⌘-Opt-D. </notification> <notification name="FirstSculptedPrim"> Você está editando uma primitiva esculpida. diff --git a/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml b/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml index ec46c8b99c..621e99c9c5 100644 --- a/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml +++ b/indra/newview/skins/default/xui/pt/panel_preferences_graphics1.xml @@ -3,7 +3,7 @@ <button label="?" name="GraphicsPreferencesHelpButton"/> <check_box label="Execute Second Life em uma janela" name="windowed mode"/> <text_editor bottom="-56" height="40" name="FullScreenInfo" width="480"> - Se desmarcado, o visualizador irá exibir em tela inteira quando fizer o acesso. + Se desmarcado, o visualizador exibirá tela inteira quando fizer o acesso. </text_editor> <text name="WindowSizeLabel"> Tamanho da Janela: @@ -56,7 +56,7 @@ rápido <text name="QualityText2"> Qualidade </text> - <check_box label="Padrão" left="395" name="CustomSettings"/> + <check_box label="Personalizar" left="395" name="CustomSettings"/> <text name="ShadersText"> Sombreadores: </text> diff --git a/indra/newview/viewer_manifest.py b/indra/newview/viewer_manifest.py index d0d97b61ec..6fc755b229 100755 --- a/indra/newview/viewer_manifest.py +++ b/indra/newview/viewer_manifest.py @@ -206,7 +206,47 @@ class WindowsManifest(ViewerManifest): self.path("openjpeg.dll")
self.end_prefix()
- # Mozilla appears to force a dependency on these files so we need to ship it (CP) - updated to vc8 versions (nyx)
+ # Plugin host application
+ if self.prefix(src='../llplugin/slplugin/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("slplugin.exe")
+ self.end_prefix()
+
+ # Media plugins - Flash (ActiveX)
+ if self.prefix(src='../media_plugins/flash_activex/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("media_plugin_flash_activex.dll")
+ self.end_prefix()
+
+ # Media plugins - QuickTime
+ if self.prefix(src='../media_plugins/quicktime/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("media_plugin_quicktime.dll")
+ self.end_prefix()
+
+ # Media plugins - WebKit/Qt
+ if self.prefix(src='../media_plugins/webkit/%s' % self.args['configuration'], dst="llplugin"):
+ self.path("media_plugin_webkit.dll")
+ self.end_prefix()
+
+ # For WebKit/Qt plugin runtimes
+ if self.prefix(src="../../libraries/i686-win32/lib/release", dst="llplugin"):
+ self.path("libeay32.dll")
+ self.path("qtcore4.dll")
+ self.path("qtgui4.dll")
+ self.path("qtnetwork4.dll")
+ self.path("qtopengl4.dll")
+ self.path("qtwebkit4.dll")
+ self.path("ssleay32.dll")
+ self.end_prefix()
+
+ # For WebKit/Qt plugin runtimes (image format plugins)
+ if self.prefix(src="../../libraries/i686-win32/lib/release/imageformats", dst="llplugin/imageformats"):
+ self.path("qgif4.dll")
+ self.path("qico4.dll")
+ self.path("qjpeg4.dll")
+ self.path("qmng4.dll")
+ self.path("qsvg4.dll")
+ self.path("qtiff4.dll")
+ self.end_prefix()
+
# These need to be installed as a SxS assembly, currently a 'private' assembly.
# See http://msdn.microsoft.com/en-us/library/ms235291(VS.80).aspx
if self.prefix(src=self.args['configuration'], dst=""):
@@ -223,34 +263,6 @@ class WindowsManifest(ViewerManifest): # The config file name needs to match the exe's name.
self.path(src="%s/secondlife-bin.exe.config" % self.args['configuration'], dst=self.final_exe() + ".config")
- # Mozilla runtime DLLs (CP)
- if self.prefix(src=self.args['configuration'], dst=""):
- self.path("freebl3.dll")
- self.path("js3250.dll")
- self.path("nspr4.dll")
- self.path("nss3.dll")
- self.path("nssckbi.dll")
- self.path("plc4.dll")
- self.path("plds4.dll")
- self.path("smime3.dll")
- self.path("softokn3.dll")
- self.path("ssl3.dll")
- self.path("xpcom.dll")
- self.path("xul.dll")
- self.end_prefix()
-
- # Mozilla runtime misc files (CP)
- if self.prefix(src="app_settings/mozilla"):
- self.path("chrome/*.*")
- self.path("components/*.*")
- self.path("greprefs/*.*")
- self.path("plugins/*.*")
- self.path("res/*.*")
- self.path("res/*/*")
- self.end_prefix()
-
- # Mozilla hack to get it to accept newer versions of msvc*80.dll than are listed in manifest
- # necessary as llmozlib2-vc80.lib refers to an old version of msvc*80.dll - can be removed when new version of llmozlib is built - Nyx
# Vivox runtimes
if self.prefix(src=self.args['configuration'], dst=""):
self.path("SLVoice.exe")
@@ -416,21 +428,11 @@ class DarwinManifest(ViewerManifest): self.path(self.args['configuration'] + "/Second Life.app", dst="")
if self.prefix(src="", dst="Contents"): # everything goes in Contents
- # Expand the tar file containing the assorted mozilla bits into
- # <bundle>/Contents/MacOS/
- self.contents_of_tar(self.args['source']+'/mozilla-universal-darwin.tgz', 'MacOS')
-
self.path("Info-SecondLife.plist", dst="Info.plist")
# copy additional libs in <bundle>/Contents/MacOS/
self.path("../../libraries/universal-darwin/lib_release/libndofdev.dylib", dst="MacOS/libndofdev.dylib")
- # replace the default theme with our custom theme (so scrollbars work).
- if self.prefix(src="mozilla-theme", dst="MacOS/chrome"):
- self.path("classic.jar")
- self.path("classic.manifest")
- self.end_prefix("MacOS/chrome")
-
# most everything goes in the Resources directory
if self.prefix(src="", dst="Resources"):
super(DarwinManifest, self).construct()
@@ -508,6 +510,18 @@ class DarwinManifest(ViewerManifest): self.path("../mac_crash_logger/" + self.args['configuration'] + "/mac-crash-logger.app", "mac-crash-logger.app")
self.path("../mac_updater/" + self.args['configuration'] + "/mac-updater.app", "mac-updater.app")
+ # plugins
+ if self.prefix(src="", dst="llplugin"):
+ self.path("../llplugin/slplugin/" + self.args['configuration'] + "/SLPlugin", "SLPlugin")
+ self.path("../media_plugins/quicktime/" + self.args['configuration'] + "/media_plugin_quicktime.dylib", "media_plugin_quicktime.dylib")
+ self.path("../media_plugins/webkit/" + self.args['configuration'] + "/media_plugin_webkit.dylib", "media_plugin_webkit.dylib")
+ self.path("../../libraries/universal-darwin/lib_release/libllwebkitlib.dylib", "libllwebkitlib.dylib")
+
+ self.path("../media_plugins/awesomium/" + self.args['configuration'] + "/media_plugin_awesomium.dylib", "media_plugin_awesomium.dylib")
+ self.path("../../libraries/universal-darwin/lib_release/Awesomium.framework", "Awesomium.framework")
+
+ self.end_prefix("llplugin")
+
# command line arguments for connecting to the proper grid
self.put_in_file(self.flags_list(), 'arguments.txt')
@@ -550,7 +564,7 @@ class DarwinManifest(ViewerManifest): # make sure we don't have stale files laying about
self.remove(sparsename, finalname)
- self.run_command('hdiutil create "%(sparse)s" -volname "%(vol)s" -fs HFS+ -type SPARSE -megabytes 300 -layout SPUD' % {
+ self.run_command('hdiutil create "%(sparse)s" -volname "%(vol)s" -fs HFS+ -type SPARSE -megabytes 400 -layout SPUD' % {
'sparse':sparsename,
'vol':volname})
@@ -621,12 +635,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):
@@ -689,18 +706,23 @@ 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
self.end_prefix("res-sdl")
+ # plugins
+ if self.prefix(src="", dst="bin/llplugin"):
+ self.path("../llplugin/slplugin/SLPlugin", "SLPlugin")
+ self.path("../media_plugins/webkit/libmedia_plugin_webkit.so", "libmedia_plugin_webkit.so")
+ self.path("../media_plugins/gstreamer010/libmedia_plugin_gstreamer010.so", "libmedia_plugin_quicktime.so")
+ self.end_prefix("bin/llplugin")
+
self.path("featuretable_linux.txt")
#self.path("secondlife-i686.supp")
- self.path("app_settings/mozilla-runtime-linux-i686")
-
if self.prefix("../../libraries/i686-linux/lib_release_client", dst="lib"):
#self.path("libkdu_v42R.so", "libkdu.so")
self.path("libfmod-3.75.so")
@@ -732,7 +754,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
diff --git a/indra/test_apps/llplugintest/CMakeLists.txt b/indra/test_apps/llplugintest/CMakeLists.txt new file mode 100644 index 0000000000..dd894087e6 --- /dev/null +++ b/indra/test_apps/llplugintest/CMakeLists.txt @@ -0,0 +1,364 @@ +# -*- cmake -*- + +project(llplugintest) + +include(00-Common) +include(FindOpenGL) +include(LLCommon) +include(LLPlugin) +include(Linking) +include(PluginAPI) +include(LLImage) +include(LLMath) +include(LLMessage) +include(LLRender) +include(LLWindow) +include(Glut) +include(Glui) + +include_directories( + ${LLPLUGIN_INCLUDE_DIRS} + ${LLCOMMON_INCLUDE_DIRS} + ${LLIMAGE_INCLUDE_DIRS} + ${LLMATH_INCLUDE_DIRS} + ${LLMESSAGE_INCLUDE_DIRS} + ${LLRENDER_INCLUDE_DIRS} + ${LLWINDOW_INCLUDE_DIRS} +) + +if (DARWIN) + include(CMakeFindFrameworks) + find_library(CARBON_LIBRARY Carbon) +endif (DARWIN) + +### demo_plugin + +#set(demo_plugin_SOURCE_FILES +# demo_plugin.cpp +# ) +# +#add_library(demo_plugin +# SHARED +# ${demo_plugin_SOURCE_FILES} +#) +# +#target_link_libraries(demo_plugin +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +# ${PLUGIN_API_WINDOWS_LIBRARIES} +#) +# +#add_dependencies(demo_plugin +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) +# +#if (DARWIN) +# # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name +# set_target_properties( +# demo_plugin +# PROPERTIES +# PREFIX "" +# BUILD_WITH_INSTALL_RPATH 1 +# INSTALL_NAME_DIR "@executable_path" +# ) +#endif (DARWIN) + +### plugin_host + +#set(plugin_host_SOURCE_FILES +# plugin_host.cpp +# ) +# +#add_executable(plugin_host +# WIN32 +# ${plugin_host_SOURCE_FILES} +#) +# +#set_target_properties(plugin_host +# PROPERTIES +# WIN32_EXECUTABLE +# FALSE +#) +# +#target_link_libraries(plugin_host +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +# ${PLUGIN_API_WINDOWS_LIBRARIES} +#) +# +#add_dependencies(plugin_host +# demo_plugin +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) + +### plugin_process_launcher + +#set(plugin_process_launcher_SOURCE_FILES +# plugin_process_launcher.cpp +# ) +# +#add_executable(plugin_process_launcher +# WIN32 +# ${plugin_process_launcher_SOURCE_FILES} +#) +# +#set_target_properties(plugin_process_launcher +# PROPERTIES +# WIN32_EXECUTABLE +# FALSE +#) +# +#target_link_libraries(plugin_process_launcher +# ${LLPLUGIN_LIBRARIES} +# ${LLMESSAGE_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +# ${PLUGIN_API_WINDOWS_LIBRARIES} +#) +# +#add_dependencies(plugin_process_launcher +# SLPlugin +# demo_plugin +# ${LLPLUGIN_LIBRARIES} +# ${LLMESSAGE_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) + +### media_simple_test + +#set(media_simple_test_SOURCE_FILES +# media_simple_test.cpp +# ) +# +#add_executable(media_simple_test +# WIN32 +# ${media_simple_test_SOURCE_FILES} +#) +# +#add_dependencies(media_simple_test copy_win_libs) +# +#set_target_properties(media_simple_test +# PROPERTIES +# WIN32_EXECUTABLE +# FALSE +#) +# +#target_link_libraries(media_simple_test +# ${GLUT_LIBRARY} +# ${OPENGL_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) + +### media_plugin_test + +#set(media_plugin_test_SOURCE_FILES +# media_plugin_test.cpp +# ) +# +#add_executable(media_plugin_test +# WIN32 +# ${media_plugin_test_SOURCE_FILES} +#) +# +#set_target_properties(media_plugin_test +# PROPERTIES +# WIN32_EXECUTABLE +# FALSE +#) +# +#target_link_libraries(media_plugin_test +# ${GLUT_LIBRARY} +# ${OPENGL_LIBRARIES} +# ${LLPLUGIN_LIBRARIES} +# ${LLMESSAGE_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +# ${PLUGIN_API_WINDOWS_LIBRARIES} +#) +# +#add_dependencies(media_plugin_test +# copy_win_libs +# SLPlugin +# demo_media_plugin +# ${LLPLUGIN_LIBRARIES} +# ${LLMESSAGE_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) + +### demo_media_plugin + +#set(demo_media_plugin_SOURCE_FILES +# demo_media_plugin.cpp +# ) +# +#add_library(demo_media_plugin +# SHARED +# ${demo_media_plugin_SOURCE_FILES} +#) +# +#target_link_libraries(demo_media_plugin +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +# ${PLUGIN_API_WINDOWS_LIBRARIES} +#) +# +#add_dependencies(demo_media_plugin +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) +# +#if (DARWIN) +# # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name +# set_target_properties( +# demo_media_plugin +# PROPERTIES +# PREFIX "" +# BUILD_WITH_INSTALL_RPATH 1 +# INSTALL_NAME_DIR "@executable_path" +# ) +#endif (DARWIN) + +### demo_media_plugin_2 + +#set(demo_media_plugin_2_SOURCE_FILES +# demo_media_plugin_2.cpp +# ) +# +#add_library(demo_media_plugin_2 +# SHARED +# ${demo_media_plugin_2_SOURCE_FILES} +#) +# +#target_link_libraries(demo_media_plugin_2 +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +# ${PLUGIN_API_WINDOWS_LIBRARIES} +#) +# +#add_dependencies(demo_media_plugin_2 +# ${LLPLUGIN_LIBRARIES} +# ${LLCOMMON_LIBRARIES} +#) +# +#if (DARWIN) +# # Don't prepend 'lib' to the executable name, and don't embed a full path in the library's install name +# set_target_properties( +# demo_media_plugin_2 +# PROPERTIES +# PREFIX "" +# BUILD_WITH_INSTALL_RPATH 1 +# INSTALL_NAME_DIR "@executable_path" +# ) +#endif (DARWIN) + +### llmediaplugintest + +set(llmediaplugintest_SOURCE_FILES + llmediaplugintest.cpp + llmediaplugintest.h + bookmarks.txt + ) + +add_executable(llmediaplugintest + WIN32 + ${llmediaplugintest_SOURCE_FILES} +) + +set_target_properties(llmediaplugintest + PROPERTIES + WIN32_EXECUTABLE + FALSE +) + +target_link_libraries(llmediaplugintest + ${GLUT_LIBRARY} + ${GLUI_LIBRARY} + ${OPENGL_LIBRARIES} + ${LLPLUGIN_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} + ${PLUGIN_API_WINDOWS_LIBRARIES} +) + +add_dependencies(llmediaplugintest + copy_win_libs + SLPlugin + media_plugin_flash_activex + media_plugin_quicktime + media_plugin_webkit + media_plugin_awesomium + ${LLPLUGIN_LIBRARIES} + ${LLMESSAGE_LIBRARIES} + ${LLCOMMON_LIBRARIES} +) + +# turn off weird GLUI pragma +add_definitions(-DGLUI_NO_LIB_PRAGMA) + +if (DARWIN OR LINUX) + # glui.h contains code that triggers the "overloaded-virtual" warning in gcc. + set_source_files_properties(llmediaplugintest.cpp PROPERTIES COMPILE_FLAGS "-Wno-overloaded-virtual") +endif (DARWIN OR LINUX) + +# Gather build products of the various dependencies into the build directory for the testbed. + +get_target_property(BUILT_SLPLUGIN SLPlugin LOCATION) +add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_SLPLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${BUILT_SLPLUGIN} +) + +if (DARWIN OR WINDOWS) + get_target_property(BUILT_WEBKIT_PLUGIN media_plugin_webkit LOCATION) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_WEBKIT_PLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${BUILT_WEBKIT_PLUGIN} + ) + + get_target_property(BUILT_QUICKTIME_PLUGIN media_plugin_quicktime LOCATION) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_QUICKTIME_PLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${BUILT_QUICKTIME_PLUGIN} + ) + + get_target_property(BUILT_AWESOMIUM_PLUGIN media_plugin_awesomium LOCATION) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_AWESOMIUM_PLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${BUILT_AWESOMIUM_PLUGIN} + ) + + # copy over bookmarks file if llmediaplugintest gets built + get_target_property(BUILT_LLMEDIAPLUGINTEST llmediaplugintest LOCATION) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/ + DEPENDS ${BUILT_LLMEDIAPLUGINTEST} + ) + # also copy it to the build configuration directory, which is what the mac wants... + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/bookmarks.txt ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${BUILT_LLMEDIAPLUGINTEST} + ) +endif (DARWIN OR WINDOWS) + +if (WINDOWS) + get_target_property(BUILT_FLASH_ACTIVEX_PLUGIN media_plugin_flash_activex LOCATION) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${BUILT_FLASH_ACTIVEX_PLUGIN} ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${BUILT_FLASH_ACTIVEX_PLUGIN} + ) + +endif (WINDOWS) + +if (DARWIN) + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllwebkitlib.dylib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/libllwebkitlib.dylib + ) + + add_custom_command(TARGET llmediaplugintest POST_BUILD + COMMAND rsync -av --delete ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/Awesomium.framework ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/ + DEPENDS ${CMAKE_SOURCE_DIR}/../libraries/universal-darwin/lib_release/Awesomium.framework + ) +endif (DARWIN) + + diff --git a/indra/test_apps/llplugintest/bookmarks.txt b/indra/test_apps/llplugintest/bookmarks.txt new file mode 100644 index 0000000000..3de56db532 --- /dev/null +++ b/indra/test_apps/llplugintest/bookmarks.txt @@ -0,0 +1,38 @@ +# format is description, url (don't put ',' chars in description :) +# if no ',' found, whole line is used for both description and url +(WK) Google Home Page,http://www.google.com +(WK) BBC News Home Page,http://news.bbc.co.uk +(WK) Second Life,http://secondlife.com +(WK) WebKit Home ,http://www.webkit.org +(WK) Yahoo News,http://news.yahoo.com +(WK) Canvas Paint (DHTML version of MS Paint),http://www.canvaspaint.org +(WK) DHTML Lemmings!,http://www.elizium.nu/scripts/lemmings/ +(WK) DHTML graphics demos,http://www.dhteumeuleu.com/ +(WK) Neat Javascript 3D,http://gyu.que.jp/jscloth/ +(QT) Local sample,file:///C|/Program Files/QuickTime/Sample.mov +(FLASH) Falling Bush,http://de.fishki.net/video/bush.swf +(FLASH) Bubble Shooter,http://www.wiicade.com/Data/72/game.swf +(FLASH) MAME,http://yvern.com/fMAME/testDecryptLoader.swf +(FLASH) Scribd doc,http://documents.scribd.com/ScribdViewer.swf?document_id=7470987&access_key=key-gemg40ut1qjepsgrx9y&page=&version=1&auto_size=true&viewMode= +(FLASH) GE Whiteboard,http://imagination3.com/pen_main.swf +(FLASH) DabbleBoard,http://www.dabbleboard.com/swf/db.swf +(FLASH) Countdown,http://itv.jaildog.com/sites/itv/ivbg/countdown.swf +(FLASH) Blockbusters,http://itv.jaildog.com/sites/itv/ivbg/blockbusters.swf +(FLASH) Clock Block,http://www.subliminalmessages.com/images/clockblock.swf +(FLASH) Devil Cat YouYube movie,http://www.youtube.com/swf/l.swf?swf=http%3A//s.ytimg.com/yt/swf/cps-vfl99456.swf&video_id=YRyK_1g4VWQ&rel=1&eurl=&iurl=http%3A//i2.ytimg.com/vi/YRyK_1g4VWQ/hqdefault.jpg&sk=70SiFZ9wC2NTIYkBXg_bK8xZB6OHmmfwC&fexp=900503%2C900037&fs=1&hl=en&autoplay=1&cr=US&avg_rating=4.56172839506&length_seconds=22&allow_ratings=1&allow_embed=1&title=devil%20cat +(QT) Movie - Watchmen Trailer,http://movies.apple.com/movies/wb/watchmen/watchmen-tlr2_480p.mov +(QT) Movie - Transformers - Revenge of the Fallen,http://movies.apple.com/movies/paramount/transformers2/transformersrevengeofthefallen-tlr1_h.320.mov +(QT) Movie - Terminator Salvation,http://movies.apple.com/movies/wb/terminatorsalvation/terminatorsalvation-tlr3_h.320.mov +(QT) Movie - Angels and Demons,http://movies.apple.com/movies/sony_pictures/angelsanddemons/angelsanddemons-video_h.320.mov +(QT) Movie - Sin City Trailer,http://movies.apple.com/movies/miramax/sin_city/sin_city_480.mov +(QT) Movie - The Incredibles Trailer,http://movies.apple.com/movies/disney/the_incredibles/the_incredibles-tlr_a480.mov +(QT) Movie - Streaming Apple Event,http://stream.qtv.apple.com/events/mar/0903lajkszg/m_090374535329zdwg_650_ref.mov +(QT) Movie - MPEG-4 from Amazon S3,http://s3.amazonaws.com/callum-linden/flashdemo/interactive_flash_demo.mp4 +(QT) Movie - Star Trek,http://movies.apple.com/movies/paramount/star_trek/startrek-tlr3_h.320.mov +(QT) Movie - Ice Age 3,http://movies.apple.com/movies/fox/ice_age_iii/iceage3-tlrd_h.320.mov +(QT) Movie - AstroBoy,http://movies.apple.com/movies/summit/astroboy/astroboy-tsr_h.320.mov +(QT) Movie - Ante Up,http://movies.apple.com/movies/independent/anteup/anteup_h.320.mov +(QT) Movie - Every Little Step,http://movies.apple.com/movies/sony/everylittlestep/everylittlestep-clip_h.320.mov +(QT) Movie - The Informers,http://movies.apple.com/movies/independent/theinformers/theinformers_h.320.mov +(QT) Animated GIF,http://upload.wikimedia.org/wikipedia/commons/4/44/Optical.greysquares.arp-animated.gif +(QT) Apple Text Descriptors,http://ubrowser.com/tmp/apple_text.txt diff --git a/indra/test_apps/llplugintest/llmediaplugintest.cpp b/indra/test_apps/llplugintest/llmediaplugintest.cpp new file mode 100644 index 0000000000..7a84ef83cb --- /dev/null +++ b/indra/test_apps/llplugintest/llmediaplugintest.cpp @@ -0,0 +1,2168 @@ +/** + * @file LLMediaPluginTest2.cpp + * @brief Primary test application for LLMedia (Separate Process) Plugin system + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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 "indra_constants.h" + +#include "llapr.h" +#include "llerrorcontrol.h" + +#include <math.h> +#include <iomanip> +#include <sstream> +#include <ctime> + +#include "llmediaplugintest.h" + +#if __APPLE__ + #include <GLUT/glut.h> +#else + #define FREEGLUT_STATIC + #include "GL/freeglut.h" + #define GLUI_FREEGLUT +#endif + +#if LL_WINDOWS +#pragma warning(disable: 4263) +#pragma warning(disable: 4264) +#endif +#include "glui.h" + + +LLMediaPluginTest* gApplication = 0; +static void gluiCallbackWrapper( int control_id ); + +//////////////////////////////////////////////////////////////////////////////// +// +static bool isTexture( GLuint texture ) +{ + bool result = false; + + // glIsTexture will sometimes return false for real textures... do this instead. + if(texture != 0) + result = true; + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::mediaPanel() +{ + mMediaTextureHandle = 0; + mPickTextureHandle = 0; + mMediaSource = NULL; + mPickTexturePixels = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel::~mediaPanel() +{ + // delete OpenGL texture handles + if ( isTexture( mPickTextureHandle ) ) + { + std::cerr << "remMediaPanel: deleting pick texture " << mPickTextureHandle << std::endl; + glDeleteTextures( 1, &mPickTextureHandle ); + mPickTextureHandle = 0; + } + + if ( isTexture( mMediaTextureHandle ) ) + { + std::cerr << "remMediaPanel: deleting media texture " << mMediaTextureHandle << std::endl; + glDeleteTextures( 1, &mMediaTextureHandle ); + mMediaTextureHandle = 0; + } + + if(mPickTexturePixels) + { + delete mPickTexturePixels; + } + + if(mMediaSource) + { + delete mMediaSource; + } + +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLMediaPluginTest::LLMediaPluginTest( int app_window, int window_width, int window_height ) : + mVersionMajor( 2 ), + mVersionMinor( 0 ), + mVersionPatch( 0 ), + mMaxPanels( 16 ), + mViewportAspect( 0 ), + mAppWindow( app_window ), + mCurMouseX( 0 ), + mCurMouseY( 0 ), + mFuzzyMedia( true ), + mSelectedPanel( 0 ), + mMediaBrowserControlEnableCookies( 0 ), + mMediaBrowserControlBackButton( 0 ), + mMediaBrowserControlForwardButton( 0 ), + mMediaTimeControlVolume( 100 ), + mMediaTimeControlSeekSeconds( 0 ), + mGluiMediaTimeControlWindowFlag( true ), + mGluiMediaBrowserControlWindowFlag( true ), + mMediaBrowserControlBackButtonFlag( true ), + mMediaBrowserControlForwardButtonFlag( true ), + mHomeWebUrl( "http://www.google.com/" ) +{ + // debugging spam + std::cout << std::endl << " GLUT version: " << "3.7.6" << std::endl; // no way to get real version from GLUT + std::cout << std::endl << " GLUI version: " << GLUI_Master.get_version() << std::endl; + std::cout << std::endl << "Media Plugin Test version: " << mVersionMajor << "." << mVersionMinor << "." << mVersionPatch << std::endl; + + // bookmark title + mBookmarks.push_back( std::pair< std::string, std::string >( "--- Bookmarks ---", "" ) ); + + // insert hardcoded URLs here as required for testing + //mBookmarks.push_back( std::pair< std::string, std::string >( "description", "url" ) ); + + // read bookmarks from file. + // note: uses command in ./CmakeLists.txt which copies bookmmarks file from source directory + // to app directory (WITHOUT build configuration dir) (this is cwd in Windows within MSVC) + // For example, test_apps\llplugintest and not test_apps\llplugintest\Release + // This may need to be changed for Mac/Linux builds. + // See https://jira.lindenlab.com/browse/DEV-31350 for large list of media URLs from AGNI + const std::string bookmarks_filename( "bookmarks.txt" ); + std::ifstream file_handle( bookmarks_filename.c_str() ); + if ( file_handle.is_open() ) + { + std::cout << "Reading bookmarks for test" << std::endl; + while( ! file_handle.eof() ) + { + std::string line; + std::getline( file_handle, line ); + if ( file_handle.eof() ) + break; + + if ( line.substr( 0, 1 ) != "#" ) + { + size_t comma_pos = line.find_first_of( ',' ); + if ( comma_pos != std::string::npos ) + { + std::string description = line.substr( 0, comma_pos ); + std::string url = line.substr( comma_pos + 1 ); +#if LL_DARWIN || LL_LINUX + // Don't load flash movies on mac or linux yet. + if ( url.find( ".swf" ) != std::string::npos ) + { + continue; + } +#endif + mBookmarks.push_back( std::pair< std::string, std::string >( description, url ) ); + } + else + { + mBookmarks.push_back( std::pair< std::string, std::string >( line, line ) ); + }; + }; + }; + std::cout << "Read " << mBookmarks.size() << " bookmarks" << std::endl; + } + else + { + std::cout << "Unable to read bookmarks from file: " << bookmarks_filename << std::endl; + }; + + // initialize linden lab APR module + ll_init_apr(); + + // Set up llerror logging + { + LLError::initForApplication("."); + LLError::setDefaultLevel(LLError::LEVEL_INFO); +// LLError::setTagLevel("Plugin", LLError::LEVEL_DEBUG); + } + + // lots of randomness in this app + srand( ( unsigned int )time( 0 ) ); + + // build GUI + makeChrome(); + + // OpenGL initialilzation + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClearDepth( 1.0f ); + glEnable( GL_DEPTH_TEST ); + glEnable( GL_COLOR_MATERIAL ); + glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); + glDepthFunc( GL_LEQUAL ); + glEnable( GL_TEXTURE_2D ); + glDisable( GL_BLEND ); + glColor3f( 1.0f, 1.0f, 1.0f ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 ); + + // start with a sane view + resetView(); + + // initial media panel + const int num_initial_panels = 4; + for( int i = 0; i < num_initial_panels; ++i ) + { + //addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + }; + + // always add a Web panel for testing + addMediaPanel( "http://www.google.com" ); + //addMediaPanel( "http://www.wiicade.com/Data/72/game.swf" ); + //addMediaPanel( "http://movies.apple.com/movies/wb/watchmen/watchmen-tlr2_480p.mov" ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLMediaPluginTest::~LLMediaPluginTest() +{ + // delete all media panels + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + remMediaPanel( mMediaPanels[ i ] ); + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::reshape( int width, int height ) +{ + // update viewport (the active window inside the chrome) + int viewport_x, viewport_y; + int viewport_height, viewport_width; + GLUI_Master.get_viewport_area( &viewport_x, &viewport_y, &viewport_width, &viewport_height ); + mViewportAspect = (float)( viewport_width ) / (float)( viewport_height ); + glViewport( viewport_x, viewport_y, viewport_width, viewport_height ); + + // save these as we'll need them later + mWindowWidth = width; + mWindowHeight = height; + + // adjust size of URL bar so it doesn't get clipped + mUrlEdit->set_w( mWindowWidth - 360 ); + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // trigger re-display + glutPostRedisplay(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::bindTexture(GLuint texture, GLint row_length, GLint alignment) +{ + glEnable( GL_TEXTURE_2D ); + +// std::cerr << "binding texture " << texture << std::endl; + + glBindTexture( GL_TEXTURE_2D, texture ); + glPixelStorei( GL_UNPACK_ROW_LENGTH, row_length ); + glPixelStorei( GL_UNPACK_ALIGNMENT, alignment ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLMediaPluginTest::checkGLError(char *name) +{ + bool result = false; + GLenum error = glGetError(); + + if(error != GL_NO_ERROR) + { + // For some reason, glGenTextures is returning GL_INVALID_VALUE... + std::cout << name << " ERROR 0x" << std::hex << error << std::dec << std::endl; + result = true; + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::drawGeometry( int panel ) +{ + // texture coordinates for each panel + GLfloat non_opengl_texture_coords[ 8 ] = { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f }; + GLfloat opengl_texture_coords[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + + GLfloat *texture_coords = mMediaPanels[ panel ]->mAppTextureCoordsOpenGL?opengl_texture_coords:non_opengl_texture_coords; + + // base coordinates for each panel + GLfloat base_vertex_pos[ 8 ] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f }; + + // calculate posiitons + const int num_panels = (int)mMediaPanels.size(); + const int num_rows = (int)sqrt( (float)num_panels ); + const int num_cols = num_panels / num_rows; + const int panel_x = ( panel / num_rows ); + const int panel_y = ( panel % num_rows ); + + const float spacing = 0.1f; + const GLfloat offset_x = num_cols * ( 1.0 + spacing ) / 2; + const GLfloat offset_y = num_rows * ( 1.0 + spacing ) / 2; + + // Adjust for media aspect ratios + { + float aspect = 1.0f; + + if(mMediaPanels[ panel ]->mMediaHeight != 0) + { + aspect = (float)mMediaPanels[ panel ]->mMediaWidth / (float)mMediaPanels[ panel ]->mMediaHeight; + } + + if(aspect > 1.0f) + { + // media is wider than it is high -- adjust the top and bottom in + for( int corner = 0; corner < 4; ++corner ) + { + float temp = base_vertex_pos[corner * 2 + 1]; + + if(temp < 0.5f) + temp += 0.5 - (0.5f / aspect); + else + temp -= 0.5 - (0.5f / aspect); + + base_vertex_pos[corner * 2 + 1] = temp; + } + } + else if(aspect < 1.0f) + { + // media is higher than it is wide -- adjust the left and right sides in + for( int corner = 0; corner < 4; ++corner ) + { + float temp = base_vertex_pos[corner * 2]; + + if(temp < 0.5f) + temp += 0.5f - (0.5f * aspect); + else + temp -= 0.5f - (0.5f * aspect); + + base_vertex_pos[corner * 2] = temp; + } + } + } + + glBegin( GL_QUADS ); + for( int corner = 0; corner < 4; ++corner ) + { + glTexCoord2f( texture_coords[ corner * 2 ], texture_coords[ corner * 2 + 1 ] ); + GLfloat x = base_vertex_pos[ corner * 2 ] + panel_x * ( 1.0 + spacing ) - offset_x + spacing / 2.0f; + GLfloat y = base_vertex_pos[ corner * 2 + 1 ] + panel_y * ( 1.0 + spacing ) - offset_y + spacing / 2.0f; + + glVertex3f( x, y, 0.0f ); + }; + glEnd(); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::startPanelHighlight( float red, float green, float blue, float line_width ) +{ + glPushAttrib( GL_ALL_ATTRIB_BITS ); + glEnable( GL_POLYGON_OFFSET_FILL ); + glPolygonOffset( -2.5f, -2.5f ); + glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + glLineWidth( line_width ); + glColor3f( red, green, blue ); + glDisable( GL_TEXTURE_2D ); +} + +////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::endPanelHighlight() +{ + glPopAttrib(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::draw( int draw_type ) +{ + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + // drawing pick texture + if ( draw_type == DrawTypePickTexture ) + { + // only bother with pick if we have something to render + // Actually, we need to pick even if we're not ready to render. + // Otherwise you can't select and remove a panel which has gone bad. +// if ( mMediaPanels[ panel ]->mReadyToRender ) + { + glMatrixMode( GL_TEXTURE ); + glPushMatrix(); + + // pick texture is a power of 2 so no need to scale + glLoadIdentity(); + + // bind to media texture + glLoadIdentity(); + bindTexture( mMediaPanels[ panel ]->mPickTextureHandle ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + + // draw geometry using pick texture + drawGeometry( panel ); + + glMatrixMode( GL_TEXTURE ); + glPopMatrix(); + }; + } + else + if ( draw_type == DrawTypeMediaTexture ) + { + bool texture_valid = false; + bool plugin_exited = false; + + if(mMediaPanels[ panel ]->mMediaSource) + { + texture_valid = mMediaPanels[ panel ]->mMediaSource->textureValid(); + plugin_exited = mMediaPanels[ panel ]->mMediaSource->isPluginExited(); + } + + // save texture matrix (changes for each panel) + glMatrixMode( GL_TEXTURE ); + glPushMatrix(); + + // only process texture if the media is ready to draw + // (we still want to draw the geometry) + if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) + { + // bind to media texture + bindTexture( mMediaPanels[ panel ]->mMediaTextureHandle ); + + if ( mFuzzyMedia ) + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + } + + // scale to fit panel + glScalef( mMediaPanels[ panel ]->mTextureScaleX, + mMediaPanels[ panel ]->mTextureScaleY, + 1.0f ); + }; + + float intensity = plugin_exited?0.25f:1.0f; + + // highlight the selected panel + if ( mSelectedPanel && ( mMediaPanels[ panel ]->mId == mSelectedPanel->mId ) ) + { + startPanelHighlight( intensity, intensity, 0.0f, 5.0f ); + drawGeometry( panel ); + endPanelHighlight(); + } + else + // this panel not able to render yet since it + // doesn't have enough information + if ( !mMediaPanels[ panel ]->mReadyToRender ) + { + startPanelHighlight( intensity, 0.0f, 0.0f, 2.0f ); + drawGeometry( panel ); + endPanelHighlight(); + } + else + // just display a border around the media + { + startPanelHighlight( 0.0f, intensity, 0.0f, 2.0f ); + drawGeometry( panel ); + endPanelHighlight(); + }; + + if ( mMediaPanels[ panel ]->mReadyToRender && texture_valid ) + { + // draw visual geometry + drawGeometry( panel ); + } + + // restore texture matrix (changes for each panel) + glMatrixMode( GL_TEXTURE ); + glPopMatrix(); + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::display() +{ + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // start with a clean slate + glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // set up OpenGL view + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glFrustum( -mViewportAspect * 0.04f, mViewportAspect * 0.04f, -0.04f, 0.04f, 0.1f, 50.0f ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glTranslatef( 0.0, 0.0, 0.0f ); + glTranslatef( mViewPos[ 0 ], mViewPos[ 1 ], -mViewPos[ 2 ] ); + glMultMatrixf( mViewRotation ); + + // draw pick texture + draw( DrawTypePickTexture ); + + // read colors and get coordinate values + glReadPixels( mCurMouseX, mCurMouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE, mPixelReadColor ); + + // clear the pick render (otherwise it may depth-fight with the textures rendered later) + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + // draw visible geometry + draw( DrawTypeMediaTexture ); + + glutSwapBuffers(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::idle() +{ +// checkGLError("LLMediaPluginTest::idle"); + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // random creation/destruction of panels enabled? + const time_t panel_timeout_time = 5; + if ( mRandomPanelCount ) + { + // time for a change + static time_t last_panel_time = 0; + if ( time( NULL ) - last_panel_time > panel_timeout_time ) + { + if ( rand() % 2 == 0 ) + { + if ( mMediaPanels.size() < 16 ) + { + std::cout << "Randomly adding new panel" << std::endl; + addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + }; + } + else + { + if ( mMediaPanels.size() > 0 ) + { + std::cout << "Deleting selected panel" << std::endl; + remMediaPanel( mSelectedPanel ); + }; + }; + time( &last_panel_time ); + }; + }; + + // random selection of bookmarks enabled? + const time_t bookmark_timeout_time = 5; + if ( mRandomBookmarks ) + { + // time for a change + static time_t last_bookmark_time = 0; + if ( time( NULL ) - last_bookmark_time > bookmark_timeout_time ) + { + // go to a different random bookmark on each panel + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + std::string uri = mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second; + + std::cout << "Random: navigating to : " << uri << std::endl; + + std::string mime_type = mimeTypeFromUrl( uri ); + + if ( mime_type != mMediaPanels[ panel ]->mMimeType ) + { + replaceMediaPanel( mMediaPanels[ panel ], uri ); + } + else + { + mMediaPanels[ panel ]->mMediaSource->loadURI( uri ); + mMediaPanels[ panel ]->mMediaSource->start(); + }; + }; + + time( &last_bookmark_time ); + }; + }; + + // update UI + if ( mSelectedPanel ) + { + // set volume based on slider if we have time media +// if ( mGluiMediaTimeControlWindowFlag ) +// { +// mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); +// }; + + // NOTE: it is absurd that we need cache the state of GLUI controls + // but enabling/disabling controls drags framerate from 500+ + // down to 15. Not a problem for plugin system - only this test + // enable/disable time based UI controls based on type of plugin + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + { + if ( ! mGluiMediaTimeControlWindowFlag ) + { + mGluiMediaTimeControlWindow->enable(); + mGluiMediaTimeControlWindowFlag = true; + }; + } + else + { + if ( mGluiMediaTimeControlWindowFlag ) + { + mGluiMediaTimeControlWindow->disable(); + mGluiMediaTimeControlWindowFlag = false; + }; + }; + + // enable/disable browser based UI controls based on type of plugin + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) + { + if ( ! mGluiMediaBrowserControlWindowFlag ) + { + mGluiMediaBrowserControlWindow->enable(); + mGluiMediaBrowserControlWindowFlag = true; + }; + } + else + { + if ( mGluiMediaBrowserControlWindowFlag ) + { + mGluiMediaBrowserControlWindow->disable(); + mGluiMediaBrowserControlWindowFlag = false; + }; + }; + + // enable/disable browser back button depending on browser history + if ( mSelectedPanel->mMediaSource->getHistoryBackAvailable() ) + { + if ( ! mMediaBrowserControlBackButtonFlag ) + { + mMediaBrowserControlBackButton->enable(); + mMediaBrowserControlBackButtonFlag = true; + }; + } + else + { + if ( mMediaBrowserControlBackButtonFlag ) + { + mMediaBrowserControlBackButton->disable(); + mMediaBrowserControlBackButtonFlag = false; + }; + }; + + // enable/disable browser forward button depending on browser history + if ( mSelectedPanel->mMediaSource->getHistoryForwardAvailable() ) + { + if ( ! mMediaBrowserControlForwardButtonFlag ) + { + mMediaBrowserControlForwardButton->enable(); + mMediaBrowserControlForwardButtonFlag = true; + }; + } + else + { + if ( mMediaBrowserControlForwardButtonFlag ) + { + mMediaBrowserControlForwardButton->disable(); + mMediaBrowserControlForwardButtonFlag = false; + }; + }; + + // NOTE: This is *very* slow and not worth optimising + updateStatusBar(); + }; + + // update all the panels + for( int panel_index = 0; panel_index < (int)mMediaPanels.size(); ++panel_index ) + { + mediaPanel *panel = mMediaPanels[ panel_index ]; + + // call plugins idle function so it can potentially update itself + panel->mMediaSource->idle(); + + // update each media panel + updateMediaPanel( panel ); + + LLRect dirty_rect; + if ( ! panel->mMediaSource->textureValid() ) + { + //std::cout << "texture invalid, skipping update..." << std::endl; + } + else + if ( panel && + ( panel->mMediaWidth != panel->mMediaSource->getWidth() || + panel->mMediaHeight != panel->mMediaSource->getHeight() ) ) + { + //std::cout << "Resize in progress, skipping update..." << std::endl; + } + else + if ( panel->mMediaSource->getDirty( &dirty_rect ) ) + { + const unsigned char* pixels = panel->mMediaSource->getBitsData(); + if ( pixels && isTexture(panel->mMediaTextureHandle)) + { + int x_offset = dirty_rect.mLeft; + int y_offset = dirty_rect.mBottom; + int width = dirty_rect.mRight - dirty_rect.mLeft; + int height = dirty_rect.mTop - dirty_rect.mBottom; + + if((dirty_rect.mRight <= panel->mTextureWidth) && (dirty_rect.mTop <= panel->mTextureHeight)) + { + // Offset the pixels pointer properly + pixels += ( y_offset * panel->mMediaSource->getTextureDepth() * panel->mMediaSource->getBitsWidth() ); + pixels += ( x_offset * panel->mMediaSource->getTextureDepth() ); + + // set up texture + bindTexture( panel->mMediaTextureHandle, panel->mMediaSource->getBitsWidth() ); + if ( mFuzzyMedia ) + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + } + else + { + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + }; + + checkGLError("glTexParameteri"); + + if(panel->mMediaSource->getTextureFormatSwapBytes()) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); + checkGLError("glPixelStorei"); + } + + // draw portion that changes into texture + glTexSubImage2D( GL_TEXTURE_2D, 0, + x_offset, + y_offset, + width, + height, + panel->mMediaSource->getTextureFormatPrimary(), + panel->mMediaSource->getTextureFormatType(), + pixels ); + + if(checkGLError("glTexSubImage2D")) + { + std::cerr << " panel ID=" << panel->mId << std::endl; + std::cerr << " texture size = " << panel->mTextureWidth << " x " << panel->mTextureHeight << std::endl; + std::cerr << " media size = " << panel->mMediaWidth << " x " << panel->mMediaHeight << std::endl; + std::cerr << " dirty rect = " << dirty_rect.mLeft << ", " << dirty_rect.mBottom << ", " << dirty_rect.mRight << ", " << dirty_rect.mTop << std::endl; + std::cerr << " texture width = " << panel->mMediaSource->getBitsWidth() << std::endl; + std::cerr << " format primary = 0x" << std::hex << panel->mMediaSource->getTextureFormatPrimary() << std::dec << std::endl; + std::cerr << " format type = 0x" << std::hex << panel->mMediaSource->getTextureFormatType() << std::dec << std::endl; + std::cerr << " pixels = " << (void*)pixels << std::endl; + } + + if(panel->mMediaSource->getTextureFormatSwapBytes()) + { + glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); + checkGLError("glPixelStorei"); + } + + panel->mMediaSource->resetDirty(); + + panel->mReadyToRender = true; + } + else + { + std::cerr << "dirty rect is outside current media size, skipping update" << std::endl; + } + }; + }; + }; + + // GLUI requires this + if ( glutGetWindow() != mAppWindow ) + glutSetWindow( mAppWindow ); + + // trigger re-display + glutPostRedisplay(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::windowPosToTexturePos( int window_x, int window_y, + int& media_x, int& media_y, + int& id ) +{ + if ( ! mSelectedPanel ) + { + media_x = 0; + media_y = 0; + id = 0; + return; + }; + + // record cursor poisiton for a readback next frame + mCurMouseX = window_x; + // OpenGL app == coordinate system this way + // NOTE: unrelated to settings in plugin - this + // is just for this app + mCurMouseY = mWindowHeight - window_y; + + // extract x (0..1023, y (0..1023) and id (0..15) from RGB components + unsigned long pixel_read_color_bits = ( mPixelReadColor[ 0 ] << 16 ) | ( mPixelReadColor[ 1 ] << 8 ) | mPixelReadColor[ 2 ]; + int texture_x = pixel_read_color_bits & 0x3ff; + int texture_y = ( pixel_read_color_bits >> 10 ) & 0x3ff; + id = ( pixel_read_color_bits >> 20 ) & 0x0f; + + // scale to size of media (1024 because we use 10 bits for X and Y from 24) + media_x = (int)( ( (float)mSelectedPanel->mMediaWidth * (float)texture_x ) / 1024.0f ); + media_y = (int)( ( (float)mSelectedPanel->mMediaHeight * (float)texture_y ) / 1024.0f ); + + // we assume the plugin uses an inverted coordinate scheme like OpenGL + // if not, the plugin code inverts the Y coordinate for us - we don't need to + media_y = mSelectedPanel->mMediaHeight - media_y; + + if ( media_x > 0 && media_y > 0 ) + { + //std::cout << " mouse coords: " << mCurMouseX << " x " << mCurMouseY << " and id = " << id << std::endl; + //std::cout << "raw texture coords: " << texture_x << " x " << texture_y << " and id = " << id << std::endl; + //std::cout << " media coords: " << media_x << " x " << media_y << " and id = " << id << std::endl; + //std::cout << std::endl; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::selectPanelById( int id ) +{ + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + if ( mMediaPanels[ panel ]->mId == id ) + { + selectPanel(mMediaPanels[ panel ]); + return; + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::selectPanel( mediaPanel* panel ) +{ + if( mSelectedPanel == panel ) + return; + + // turn off volume before we delete it + if( mSelectedPanel && mSelectedPanel->mMediaSource ) + { + mSelectedPanel->mMediaSource->setVolume( 0.0f ); + mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_LOW ); + }; + + mSelectedPanel = panel; + + if( mSelectedPanel && mSelectedPanel->mMediaSource ) + { + mSelectedPanel->mMediaSource->setVolume( (float)mMediaTimeControlVolume / 100.0f ); + mSelectedPanel->mMediaSource->setPriority( LLPluginClassMedia::PRIORITY_NORMAL ); + + if(!mSelectedPanel->mStartUrl.empty()) + { + mUrlEdit->set_text(const_cast<char*>(mSelectedPanel->mStartUrl.c_str()) ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +mediaPanel* LLMediaPluginTest::findMediaPanel( LLPluginClassMedia* source ) +{ + mediaPanel *result = NULL; + + for( int panel = 0; panel < (int)mMediaPanels.size(); ++panel ) + { + if ( mMediaPanels[ panel ]->mMediaSource == source ) + { + result = mMediaPanels[ panel ]; + } + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::navigateToNewURI( std::string uri ) +{ + if ( uri.length() ) + { + std::string mime_type = mimeTypeFromUrl( uri ); + + if ( !mSelectedPanel->mMediaSource->isPluginExited() && (mime_type == mSelectedPanel->mMimeType) ) + { + std::cout << "MIME type is the same" << std::endl; + mSelectedPanel->mMediaSource->loadURI( uri ); + mSelectedPanel->mMediaSource->start(); + mBookmarkList->do_selection( 0 ); + } + else + { + std::cout << "MIME type changed or plugin had exited" << std::endl; + replaceMediaPanel( mSelectedPanel, uri ); + mBookmarkList->do_selection( 0 ); + } + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::initUrlHistory( std::string uris ) +{ + if ( uris.length() > 0 ) + { + std::cout << "init URL : " << uris << std::endl; + LLSD historySD; + + char *cstr, *p; + cstr = new char[uris.size()+1]; + strcpy(cstr, uris.c_str()); + const char *DELIMS = " ,;"; + p = strtok(cstr, DELIMS); + while (p != NULL) { + historySD.insert(0, p); + p = strtok(NULL, DELIMS); + } + mSelectedPanel->mMediaSource->initializeUrlHistory(historySD); + delete[] cstr; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::gluiCallback( int control_id ) +{ + if ( control_id == mIdBookmarks ) + { + std::string uri = mBookmarks[ mSelBookmark ].second; + + navigateToNewURI( uri ); + } + else + if ( control_id == mIdUrlEdit) + { + std::string uri = mUrlEdit->get_text(); + + navigateToNewURI( uri ); + } + else + if ( control_id == mIdUrlInitHistoryEdit ) + { + std::string uri = mUrlInitHistoryEdit->get_text(); + + initUrlHistory( uri ); + } + else + if ( control_id == mIdControlAddPanel ) + { + addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + } + else + if ( control_id == mIdControlRemPanel ) + { + remMediaPanel( mSelectedPanel ); + } + else + if ( control_id == mIdDisableTimeout ) + { + // Set the "disable timeout" flag for all active plugins. + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + mMediaPanels[ i ]->mMediaSource->setDisableTimeout(mDisableTimeout); + } + } + else + if ( control_id == mIdControlCrashPlugin ) + { + // send message to plugin and ask it to crash + // (switch out for ReleaseCandidate version :) ) + if(mSelectedPanel && mSelectedPanel->mMediaSource) + { + mSelectedPanel->mMediaSource->crashPlugin(); + } + } + else + if ( control_id == mIdControlHangPlugin ) + { + // send message to plugin and ask it to hang + // (switch out for ReleaseCandidate version :) ) + if(mSelectedPanel && mSelectedPanel->mMediaSource) + { + mSelectedPanel->mMediaSource->hangPlugin(); + } + } + else + if ( control_id == mIdControlExitApp ) + { + // text for exiting plugin system cleanly + delete this; // clean up + exit( 0 ); + } + else + if ( control_id == mIdMediaTimeControlPlay ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlLoop ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( true ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlPause ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->pause(); + } + else + if ( control_id == mIdMediaTimeControlStop ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->stop(); + }; + } + else + if ( control_id == mIdMediaTimeControlSeek ) + { + if ( mSelectedPanel ) + { + // get value from spinner + float seconds_to_seek = mMediaTimeControlSeekSeconds; + mSelectedPanel->mMediaSource->seek( seconds_to_seek ); + mSelectedPanel->mMediaSource->start(); + }; + } + else + if ( control_id == mIdMediaTimeControlRewind ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(-2.0f); + }; + } + else + if ( control_id == mIdMediaTimeControlFastForward ) + { + if ( mSelectedPanel ) + { + mSelectedPanel->mMediaSource->setLoop( false ); + mSelectedPanel->mMediaSource->start(2.0f); + }; + } + else + if ( control_id == mIdMediaBrowserControlBack ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_back(); + } + else + if ( control_id == mIdMediaBrowserControlStop ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_stop(); + } + else + if ( control_id == mIdMediaBrowserControlForward ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_forward(); + } + else + if ( control_id == mIdMediaBrowserControlHome ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->loadURI( mHomeWebUrl ); + } + else + if ( control_id == mIdMediaBrowserControlReload ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->browse_reload( true ); + } + else + if ( control_id == mIdMediaBrowserControlClearCache ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->clear_cache(); + } + else + if ( control_id == mIdMediaBrowserControlClearCookies ) + { + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->clear_cookies(); + } + else + if ( control_id == mIdMediaBrowserControlEnableCookies ) + { + if ( mSelectedPanel ) + { + if ( mMediaBrowserControlEnableCookies ) + { + mSelectedPanel->mMediaSource->enable_cookies( true ); + } + else + { + mSelectedPanel->mMediaSource->enable_cookies( false ); + } + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::keyboard( int key ) +{ + //if ( key == 'a' || key == 'A' ) + // addMediaPanel( mBookmarks[ rand() % ( mBookmarks.size() - 1 ) + 1 ].second ); + //else + //if ( key == 'r' || key == 'R' ) + // remMediaPanel( mSelectedPanel ); + //else + //if ( key == 'd' || key == 'D' ) + // dumpPanelInfo(); + //else + if ( key == 27 ) + { + std::cout << "Application finished - exiting..." << std::endl; + delete this; + exit( 0 ); + }; + + mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_DOWN, key, 0 ); + mSelectedPanel->mMediaSource->keyEvent( LLPluginClassMedia::KEY_EVENT_UP, key, 0 ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::mouseButton( int button, int state, int x, int y ) +{ + if ( button == GLUT_LEFT_BUTTON ) + { + if ( state == GLUT_DOWN ) + { + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_DOWN, media_x, media_y, 0 ); + } + else + if ( state == GLUT_UP ) + { + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + // only select a panel if we're on a panel + // (HACK: strictly speaking this rules out clicking on + // the origin of a panel but that's very unlikely) + if ( media_x > 0 && media_y > 0 ) + { + selectPanelById( id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_UP, media_x, media_y, 0 ); + }; + }; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::mousePassive( int x, int y ) +{ + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::mouseMove( int x, int y ) +{ + int media_x, media_y, id; + windowPosToTexturePos( x, y, media_x, media_y, id ); + + if ( mSelectedPanel ) + mSelectedPanel->mMediaSource->mouseEvent( LLPluginClassMedia::MOUSE_EVENT_MOVE, media_x, media_y, 0 ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::makeChrome() +{ + // IDs used by GLUI + int start_id = 0x1000; + + // right side window - geometry manipulators +#if __APPLE__ + // the Apple GLUT implementation doesn't seem to set the graphic offset of subwindows correctly when they overlap in certain ways. + // Use a separate controls window in this case. + // GLUI window at right containing manipulation controls and other buttons + int x = glutGet(GLUT_WINDOW_X) + glutGet(GLUT_WINDOW_WIDTH) + 4; + int y = glutGet(GLUT_WINDOW_Y); + GLUI* right_glui_window = GLUI_Master.create_glui( "", 0, x, y ); +#else + GLUI* right_glui_window = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_RIGHT ); +#endif + mViewRotationCtrl = right_glui_window->add_rotation( "Rotation", mViewRotation ); + mViewTranslationCtrl = right_glui_window->add_translation( "Translate", GLUI_TRANSLATION_XY, mViewPos ); + mViewTranslationCtrl->set_speed( 0.01f ); + mViewScaleCtrl = right_glui_window->add_translation( "Scale", GLUI_TRANSLATION_Z, &mViewPos[ 2 ] ); + mViewScaleCtrl->set_speed( 0.05f ); + right_glui_window->set_main_gfx_window( mAppWindow ); + + // right side window - app controls + mIdControlAddPanel = start_id++; + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + right_glui_window->add_button( "Add panel", mIdControlAddPanel, gluiCallbackWrapper ); + right_glui_window->add_statictext( "" ); + mIdControlRemPanel = start_id++; + right_glui_window->add_button( "Rem panel", mIdControlRemPanel, gluiCallbackWrapper ); + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + mIdControlCrashPlugin = start_id++; + right_glui_window->add_button( "Crash plugin", mIdControlCrashPlugin, gluiCallbackWrapper ); + mIdControlHangPlugin = start_id++; + right_glui_window->add_button( "Hang plugin", mIdControlHangPlugin, gluiCallbackWrapper ); + + right_glui_window->add_statictext( "" ); + right_glui_window->add_separator(); + right_glui_window->add_statictext( "" ); + mIdControlExitApp = start_id++; + right_glui_window->add_button( "Exit app", mIdControlExitApp, gluiCallbackWrapper ); + + //// top window - holds bookmark UI + mIdBookmarks = start_id++; + mSelBookmark = 0; + GLUI* glui_window_top = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mBookmarkList = glui_window_top->add_listbox( "", &mSelBookmark, mIdBookmarks, gluiCallbackWrapper ); + // only add the first 50 bookmarks - list can be very long sometimes (30,000+) + // when testing list of media URLs from AGNI for example + for( unsigned int each = 0; each < mBookmarks.size() && each < 50; ++each ) + mBookmarkList->add_item( each, const_cast< char* >( mBookmarks[ each ].first.c_str() ) ); + glui_window_top->set_main_gfx_window( mAppWindow ); + + glui_window_top->add_column( false ); + mIdUrlEdit = start_id++; + mUrlEdit = glui_window_top->add_edittext( "Url:", GLUI_EDITTEXT_TEXT, 0, mIdUrlEdit, gluiCallbackWrapper ); + mUrlEdit->set_w( 600 ); + GLUI* glui_window_top2 = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mIdUrlInitHistoryEdit = start_id++; + mUrlInitHistoryEdit = glui_window_top2->add_edittext( "Init History (separate by commas or semicolons):", + GLUI_EDITTEXT_TEXT, 0, mIdUrlInitHistoryEdit, gluiCallbackWrapper ); + mUrlInitHistoryEdit->set_w( 800 ); + + // top window - media controls for "time" media types (e.g. movies) + mGluiMediaTimeControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mGluiMediaTimeControlWindow->set_main_gfx_window( mAppWindow ); + mIdMediaTimeControlPlay = start_id++; + mGluiMediaTimeControlWindow->add_button( "PLAY", mIdMediaTimeControlPlay, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlLoop = start_id++; + mGluiMediaTimeControlWindow->add_button( "LOOP", mIdMediaTimeControlLoop, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlPause = start_id++; + mGluiMediaTimeControlWindow->add_button( "PAUSE", mIdMediaTimeControlPause, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + + GLUI_Button *button; + mIdMediaTimeControlRewind = start_id++; + button = mGluiMediaTimeControlWindow->add_button( "<<", mIdMediaTimeControlRewind, gluiCallbackWrapper ); + button->set_w(30); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlFastForward = start_id++; + button = mGluiMediaTimeControlWindow->add_button( ">>", mIdMediaTimeControlFastForward, gluiCallbackWrapper ); + button->set_w(30); + + mGluiMediaTimeControlWindow->add_column( true ); + + mIdMediaTimeControlStop = start_id++; + mGluiMediaTimeControlWindow->add_button( "STOP", mIdMediaTimeControlStop, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlVolume = start_id++; + GLUI_Spinner* spinner = mGluiMediaTimeControlWindow->add_spinner( "Volume", 2, &mMediaTimeControlVolume, mIdMediaTimeControlVolume, gluiCallbackWrapper); + spinner->set_float_limits( 0, 100 ); + mGluiMediaTimeControlWindow->add_column( true ); + mIdMediaTimeControlSeekSeconds = start_id++; + spinner = mGluiMediaTimeControlWindow->add_spinner( "", 2, &mMediaTimeControlSeekSeconds, mIdMediaTimeControlSeekSeconds, gluiCallbackWrapper); + spinner->set_float_limits( 0, 200 ); + spinner->set_w( 32 ); + spinner->set_speed( 0.025f ); + mGluiMediaTimeControlWindow->add_column( false ); + mIdMediaTimeControlSeek = start_id++; + mGluiMediaTimeControlWindow->add_button( "SEEK", mIdMediaTimeControlSeek, gluiCallbackWrapper ); + mGluiMediaTimeControlWindow->add_column( false ); + + + // top window - media controls for "browser" media types (e.g. web browser) + mGluiMediaBrowserControlWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mGluiMediaBrowserControlWindow->set_main_gfx_window( mAppWindow ); + mIdMediaBrowserControlBack = start_id++; + mMediaBrowserControlBackButton = mGluiMediaBrowserControlWindow->add_button( "BACK", mIdMediaBrowserControlBack, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlStop = start_id++; + mGluiMediaBrowserControlWindow->add_button( "STOP", mIdMediaBrowserControlStop, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlForward = start_id++; + mMediaBrowserControlForwardButton = mGluiMediaBrowserControlWindow->add_button( "FORWARD", mIdMediaBrowserControlForward, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlHome = start_id++; + mGluiMediaBrowserControlWindow->add_button( "HOME", mIdMediaBrowserControlHome, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlReload = start_id++; + mGluiMediaBrowserControlWindow->add_button( "RELOAD", mIdMediaBrowserControlReload, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlClearCache = start_id++; + mGluiMediaBrowserControlWindow->add_button( "CLEAR CACHE", mIdMediaBrowserControlClearCache, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlClearCookies = start_id++; + mGluiMediaBrowserControlWindow->add_button( "CLEAR COOKIES", mIdMediaBrowserControlClearCookies, gluiCallbackWrapper ); + mGluiMediaBrowserControlWindow->add_column( false ); + mIdMediaBrowserControlEnableCookies = start_id++; + mMediaBrowserControlEnableCookies = 0; + mGluiMediaBrowserControlWindow->add_checkbox( "Enable Cookies", &mMediaBrowserControlEnableCookies, mIdMediaBrowserControlEnableCookies, gluiCallbackWrapper ); + + // top window - misc controls + GLUI* glui_window_misc_control = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_TOP ); + mIdRandomPanelCount = start_id++; + mRandomPanelCount = 0; + glui_window_misc_control->add_checkbox( "Randomize panel count", &mRandomPanelCount, mIdRandomPanelCount, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + mIdRandomBookmarks = start_id++; + mRandomBookmarks = 0; + glui_window_misc_control->add_checkbox( "Randomize bookmarks", &mRandomBookmarks, mIdRandomBookmarks, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + glui_window_misc_control->add_column( true ); + + mIdDisableTimeout = start_id++; + mDisableTimeout = 0; + glui_window_misc_control->add_checkbox( "Disable plugin timeout", &mDisableTimeout, mIdDisableTimeout, gluiCallbackWrapper ); + glui_window_misc_control->set_main_gfx_window( mAppWindow ); + + // bottom window - status + mBottomGLUIWindow = GLUI_Master.create_glui_subwindow( mAppWindow, GLUI_SUBWINDOW_BOTTOM ); + mStatusText = mBottomGLUIWindow->add_statictext( "" ); + mBottomGLUIWindow->set_main_gfx_window( mAppWindow ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::resetView() +{ + mViewRotationCtrl->reset(); + + mViewScaleCtrl->set_x( 0.0f ); + mViewScaleCtrl->set_y( 0.0f ); + mViewScaleCtrl->set_z( 3.0f ); + + mViewTranslationCtrl->set_x( 0.0f ); + mViewTranslationCtrl->set_y( 0.0f ); + mViewTranslationCtrl->set_z( 0.0f ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels ) +{ + int pick_texture_width = 1024; + int pick_texture_height = 1024; + int pick_texture_depth = 3; + unsigned char* ptr = new unsigned char[ pick_texture_width * pick_texture_height * pick_texture_depth ]; + for( int y = 0; y < pick_texture_height; ++y ) + { + for( int x = 0; x < pick_texture_width * pick_texture_depth ; x += pick_texture_depth ) + { + unsigned long bits = 0L; + bits |= ( id << 20 ) | ( y << 10 ) | ( x / 3 ); + unsigned char r_component = ( bits >> 16 ) & 0xff; + unsigned char g_component = ( bits >> 8 ) & 0xff; + unsigned char b_component = bits & 0xff; + + ptr[ y * pick_texture_width * pick_texture_depth + x + 0 ] = r_component; + ptr[ y * pick_texture_width * pick_texture_depth + x + 1 ] = g_component; + ptr[ y * pick_texture_width * pick_texture_depth + x + 2 ] = b_component; + }; + }; + + glGenTextures( 1, texture_handle ); + + checkGLError("glGenTextures"); + std::cout << "glGenTextures returned " << *texture_handle << std::endl; + + bindTexture( *texture_handle ); + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGB, + pick_texture_width, pick_texture_height, + 0, GL_RGB, GL_UNSIGNED_BYTE, ptr ); + + *texture_pixels = ptr; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMediaPluginTest::mimeTypeFromUrl( std::string& url ) +{ + // default to web + std::string mime_type = "text/html"; + + // we may need a more advanced MIME type accessor later :-) + if ( url.find( ".swf" ) != std::string::npos ) // Flash movies + mime_type = "application/x-shockwave-flash"; + else + if ( url.find( ".mov" ) != std::string::npos ) // Movies + mime_type = "video/quicktime"; + else + if ( url.find( ".txt" ) != std::string::npos ) // Apple Text descriptors + mime_type = "video/quicktime"; + + return mime_type; +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLMediaPluginTest::pluginNameFromMimeType( std::string& mime_type ) +{ +#if LL_DARWIN + std::string plugin_name( "media_plugin_null.dylib" ); + if ( mime_type == "video/quicktime" ) + plugin_name = "media_plugin_quicktime.dylib"; + else + if ( mime_type == "text/html" ) + plugin_name = "media_plugin_webkit.dylib"; + +#elif LL_WINDOWS + std::string plugin_name( "media_plugin_null.dll" ); + + if ( mime_type == "video/quicktime" ) + plugin_name = "media_plugin_quicktime.dll"; + else + if ( mime_type == "text/html" ) + plugin_name = "media_plugin_webkit.dll"; + else + if ( mime_type == "application/x-shockwave-flash" ) + plugin_name = "media_plugin_flash_activex.dll"; + +#elif LL_LINUX + std::string plugin_name( "libmedia_plugin_null.so" ); + + if ( mime_type == "video/quicktime" ) + plugin_name = "libmedia_plugin_quicktime.so"; + else + if ( mime_type == "text/html" ) + plugin_name = "libmedia_plugin_webkit.so"; +#endif + return plugin_name; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::addMediaPanel( std::string url ) +{ + // Get the plugin filename using the URL + std::string mime_type = mimeTypeFromUrl( url ); + std::string plugin_name = pluginNameFromMimeType( mime_type ); + + // create a random size for the new media + int media_width; + int media_height; + getRandomMediaSize( media_width, media_height, mime_type ); + + // make a new plugin + LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + + // tell the plugin what size we asked for + media_source->setSize( media_width, media_height ); + + // Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX + std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS + std::string launcher_name( "SLPlugin.exe" ); +#endif + media_source->init( launcher_name, plugin_name ); + media_source->setDisableTimeout(mDisableTimeout); + + // make a new panel and save parameters + mediaPanel* panel = new mediaPanel; + panel->mMediaSource = media_source; + panel->mStartUrl = url; + panel->mMimeType = mime_type; + panel->mMediaWidth = media_width; + panel->mMediaHeight = media_height; + panel->mTextureWidth = 0; + panel->mTextureHeight = 0; + panel->mTextureScaleX = 0; + panel->mTextureScaleY = 0; + panel->mMediaTextureHandle = 0; + panel->mPickTextureHandle = 0; + panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too + panel->mReadyToRender = false; + + // look through current media panels to find an unused index number + bool id_exists = true; + for( int nid = 0; nid < mMaxPanels; ++nid ) + { + // does this id exist already? + id_exists = false; + for( int pid = 0; pid < (int)mMediaPanels.size(); ++pid ) + { + if ( nid == mMediaPanels[ pid ]->mId ) + { + id_exists = true; + break; + }; + }; + + // id wasn't found so we can use it + if ( ! id_exists ) + { + panel->mId = nid; + break; + }; + }; + + // if we get here and this flag is set, there is no room for any more panels + if ( id_exists ) + { + std::cout << "No room for any more panels" << std::endl; + } + else + { + // now we have the ID we can use it to make the + // pick texture (id is baked into texture pixels) + makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + + // save this in the list of panels + mMediaPanels.push_back( panel ); + + // select the panel that was just created + selectPanel( panel ); + + // load and start the URL + panel->mMediaSource->loadURI( url ); + panel->mMediaSource->start(); + + std::cout << "Adding new media panel for " << url << "(" << media_width << "x" << media_height << ") with index " << panel->mId << " - total panels = " << mMediaPanels.size() << std::endl; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::updateMediaPanel( mediaPanel* panel ) +{ +// checkGLError("LLMediaPluginTest::updateMediaPanel"); + + if ( ! panel ) + return; + + if(!panel->mMediaSource || !panel->mMediaSource->textureValid()) + { + panel->mReadyToRender = false; + return; + } + + // take a reference copy of the plugin values since they + // might change during this lifetime of this function + int plugin_media_width = panel->mMediaSource->getWidth(); + int plugin_media_height = panel->mMediaSource->getHeight(); + int plugin_texture_width = panel->mMediaSource->getBitsWidth(); + int plugin_texture_height = panel->mMediaSource->getBitsHeight(); + + // If the texture isn't created or the media or texture dimensions changed AND + // the sizes are valid then we need to delete the old media texture (if necessary) + // then make a new one. + if ((panel->mMediaTextureHandle == 0 || + panel->mMediaWidth != plugin_media_width || + panel->mMediaHeight != plugin_media_height || + panel->mTextureWidth != plugin_texture_width || + panel->mTextureHeight != plugin_texture_height) && + ( plugin_media_width > 0 && plugin_media_height > 0 && + plugin_texture_width > 0 && plugin_texture_height > 0 ) ) + { + std::cout << "Valid media size (" << plugin_media_width << " x " << plugin_media_height + << ") and texture size (" << plugin_texture_width << " x " << plugin_texture_height + << ") for panel with ID=" << panel->mId << " - making texture" << std::endl; + + // delete old GL texture + if ( isTexture( panel->mMediaTextureHandle ) ) + { + std::cerr << "updateMediaPanel: deleting texture " << panel->mMediaTextureHandle << std::endl; + glDeleteTextures( 1, &panel->mMediaTextureHandle ); + panel->mMediaTextureHandle = 0; + } + + std::cerr << "before: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; + + // make a GL texture based on the dimensions the plugin told us + GLuint new_texture = 0; + glGenTextures( 1, &new_texture ); + + checkGLError("glGenTextures"); + + std::cout << "glGenTextures returned " << new_texture << std::endl; + + panel->mMediaTextureHandle = new_texture; + + bindTexture( panel->mMediaTextureHandle ); + + std::cout << "Setting texture size to " << plugin_texture_width << " x " << plugin_texture_height << std::endl; + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGB, + plugin_texture_width, plugin_texture_height, + 0, GL_RGB, GL_UNSIGNED_BYTE, + 0 ); + + + std::cerr << "after: pick texture is " << panel->mPickTextureHandle << ", media texture is " << panel->mMediaTextureHandle << std::endl; + }; + + // update our record of the media and texture dimensions + // NOTE: do this after we we check for sizes changes + panel->mMediaWidth = plugin_media_width; + panel->mMediaHeight = plugin_media_height; + panel->mTextureWidth = plugin_texture_width; + panel->mTextureHeight = plugin_texture_height; + if ( plugin_texture_width > 0 ) + { + panel->mTextureScaleX = (double)panel->mMediaWidth / (double)panel->mTextureWidth; + }; + if ( plugin_texture_height > 0 ) + { + panel->mTextureScaleY = (double)panel->mMediaHeight / (double)panel->mTextureHeight; + }; + + // update the flag which tells us if the media source uses OprnGL coords or not. + panel->mAppTextureCoordsOpenGL = panel->mMediaSource->getTextureCoordsOpenGL(); + + // Check to see if we have enough to render this panel. + // If we do, set a flag that the display functions use so + // they only render a panel with media if it's ready. + if ( panel->mMediaWidth < 0 || + panel->mMediaHeight < 0 || + panel->mTextureWidth < 1 || + panel->mTextureHeight < 1 || + panel->mMediaTextureHandle == 0 ) + { + panel->mReadyToRender = false; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::replaceMediaPanel( mediaPanel* panel, std::string url ) +{ + // no media panels so we can't change anything - have to add + if ( mMediaPanels.size() == 0 ) + return; + + // sanity check + if ( ! panel ) + return; + + int index; + for(index = 0; index < (int)mMediaPanels.size(); index++) + { + if(mMediaPanels[index] == panel) + break; + } + + if(index >= (int)mMediaPanels.size()) + { + // panel isn't in mMediaPanels + return; + } + + std::cout << "Replacing media panel with index " << panel->mId << std::endl; + + int panel_id = panel->mId; + + if(mSelectedPanel == panel) + mSelectedPanel = NULL; + + delete panel; + + // Get the plugin filename using the URL + std::string mime_type = mimeTypeFromUrl( url ); + std::string plugin_name = pluginNameFromMimeType( mime_type ); + + // create a random size for the new media + int media_width; + int media_height; + getRandomMediaSize( media_width, media_height, mime_type ); + + // make a new plugin + LLPluginClassMedia* media_source = new LLPluginClassMedia(this); + + // tell the plugin what size we asked for + media_source->setSize( media_width, media_height ); + + // Use the launcher start and initialize the plugin +#if LL_DARWIN || LL_LINUX + std::string launcher_name( "SLPlugin" ); +#elif LL_WINDOWS + std::string launcher_name( "SLPlugin.exe" ); +#endif + media_source->init( launcher_name, plugin_name ); + media_source->setDisableTimeout(mDisableTimeout); + + // make a new panel and save parameters + panel = new mediaPanel; + panel->mMediaSource = media_source; + panel->mStartUrl = url; + panel->mMimeType = mime_type; + panel->mMediaWidth = media_width; + panel->mMediaHeight = media_height; + panel->mTextureWidth = 0; + panel->mTextureHeight = 0; + panel->mTextureScaleX = 0; + panel->mTextureScaleY = 0; + panel->mMediaTextureHandle = 0; + panel->mPickTextureHandle = 0; + panel->mAppTextureCoordsOpenGL = false; // really need an 'undefined' state here too + panel->mReadyToRender = false; + + panel->mId = panel_id; + + // Replace the entry in the panels array + mMediaPanels[index] = panel; + + // now we have the ID we can use it to make the + // pick texture (id is baked into texture pixels) + makePickTexture( panel->mId, &panel->mPickTextureHandle, &panel->mPickTexturePixels ); + + // select the panel that was just created + selectPanel( panel ); + + // load and start the URL + panel->mMediaSource->loadURI( url ); + panel->mMediaSource->start(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::getRandomMediaSize( int& width, int& height, std::string mime_type ) +{ + // Make a new media source with a random size which we'll either + // directly or the media plugin will tell us what it wants later. + // Use a random size so we can test support for weird media sizes. + // (Almost everything else will get filled in later once the + // plugin responds) + // NB. Do we need to enforce that width is on 4 pixel boundary? + width = ( ( rand() % 170 ) + 30 ) * 4; + height = ( ( rand() % 170 ) + 30 ) * 4; + + // adjust this random size if it's a browser so we get + // a more useful size for testing.. + if ( mime_type == "text/html" ) + { + width = ( ( rand() % 100 ) + 100 ) * 4; + height = ( width * ( ( rand() % 400 ) + 1000 ) ) / 1000; + } + else + // adjust this random size if it's Flash so we get + // a more useful size for testing.. + if ( mime_type == "application/x-shockwave-flash" ) + { + width = ( ( rand() % 100 ) + 100 ) * 4; + height = ( width * ( ( rand() % 400 ) + 1000 ) ) / 1000; + }; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::remMediaPanel( mediaPanel* panel ) +{ + // always leave one panel + if ( mMediaPanels.size() == 1 ) + return; + + // sanity check - don't think this can happen but see above for a case where it might... + if ( ! panel ) + return; + + std::cout << "Removing media panel with index " << panel->mId << " - total panels = " << mMediaPanels.size() - 1 << std::endl; + + if(mSelectedPanel == panel) + mSelectedPanel = NULL; + + delete panel; + + // remove from storage list + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + if ( mMediaPanels[ i ] == panel ) + { + mMediaPanels.erase( mMediaPanels.begin() + i ); + break; + }; + }; + + // select the first panel + selectPanel( mMediaPanels[ 0 ] ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::updateStatusBar() +{ + if ( ! mSelectedPanel ) + return; + + // cache results - this is a very slow function + static int cached_id = -1; + static int cached_media_width = -1; + static int cached_media_height = -1; + static int cached_texture_width = -1; + static int cached_texture_height = -1; + static bool cached_supports_browser_media = true; + static bool cached_supports_time_media = false; + static int cached_movie_time = -1; + + static std::string cached_plugin_version = ""; + if ( + cached_id == mSelectedPanel->mId && + cached_media_width == mSelectedPanel->mMediaWidth && + cached_media_height == mSelectedPanel->mMediaHeight && + cached_texture_width == mSelectedPanel->mTextureWidth && + cached_texture_height == mSelectedPanel->mTextureHeight && + cached_supports_browser_media == mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() && + cached_supports_time_media == mSelectedPanel->mMediaSource->pluginSupportsMediaTime() && + cached_plugin_version == mSelectedPanel->mMediaSource->getPluginVersion() && + cached_movie_time == (int)mSelectedPanel->mMediaSource->getCurrentTime() + ) + { + // nothing changed so don't spend time in this shitty function + return; + }; + + std::ostringstream stream( "" ); + + stream.str( "" ); + stream.clear(); + + stream << "Id: "; + stream << std::setw( 2 ) << std::setfill( '0' ); + stream << mSelectedPanel->mId; + stream << " | "; + stream << "Media: "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << mSelectedPanel->mMediaWidth; + stream << " x "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << mSelectedPanel->mMediaHeight; + stream << " | "; + stream << "Texture: "; + stream << std::setw( 4 ) << std::setfill( '0' ); + stream << mSelectedPanel->mTextureWidth; + stream << " x "; + stream << std::setw( 4 ) << std::setfill( '0' ); + stream << mSelectedPanel->mTextureHeight; + stream << " | "; + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser() ) + stream << "BROWSER"; + else + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + stream << "TIME "; + stream << " | "; + stream << mSelectedPanel->mMediaSource->getPluginVersion(); + stream << " | "; + if ( mSelectedPanel->mMediaSource->pluginSupportsMediaTime() ) + { + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << (int)mSelectedPanel->mMediaSource->getCurrentTime(); + stream << " / "; + stream << std::setw( 3 ) << std::setfill( '0' ); + stream << (int)mSelectedPanel->mMediaSource->getDuration(); + stream << " @ "; + stream << (int)mSelectedPanel->mMediaSource->getCurrentPlayRate(); + stream << " | "; + }; + + glutSetWindow( mBottomGLUIWindow->get_glut_window_id() ); + mStatusText->set_text( const_cast< char*>( stream.str().c_str() ) ); + glutSetWindow( mAppWindow ); + + // caching + cached_id = mSelectedPanel->mId; + cached_media_width = mSelectedPanel->mMediaWidth; + cached_media_height = mSelectedPanel->mMediaHeight; + cached_texture_width = mSelectedPanel->mTextureWidth; + cached_texture_height = mSelectedPanel->mTextureHeight; + cached_supports_browser_media = mSelectedPanel->mMediaSource->pluginSupportsMediaBrowser(); + cached_supports_time_media = mSelectedPanel->mMediaSource->pluginSupportsMediaTime(); + cached_plugin_version = mSelectedPanel->mMediaSource->getPluginVersion(); + cached_movie_time = (int)mSelectedPanel->mMediaSource->getCurrentTime(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::dumpPanelInfo() +{ + std::cout << std::endl << "===== Media Panels =====" << std::endl; + for( int i = 0; i < (int)mMediaPanels.size(); ++i ) + { + std::cout << std::setw( 2 ) << std::setfill( '0' ); + std::cout << i + 1 << "> "; + std::cout << "Id: "; + std::cout << std::setw( 2 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mId; + std::cout << " | "; + std::cout << "Media: "; + std::cout << std::setw( 3 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mMediaWidth; + std::cout << " x "; + std::cout << std::setw( 3 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mMediaHeight; + std::cout << " | "; + std::cout << "Texture: "; + std::cout << std::setw( 4 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mTextureWidth; + std::cout << " x "; + std::cout << std::setw( 4 ) << std::setfill( '0' ); + std::cout << mMediaPanels[ i ]->mTextureHeight; + std::cout << " | "; + if ( mMediaPanels[ i ] == mSelectedPanel ) + std::cout << "(selected)"; + + std::cout << std::endl; + }; + std::cout << "========================" << std::endl; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLMediaPluginTest::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event) +{ + // Uncomment this to make things much, much quieter. +// return; + + switch(event) + { + case MEDIA_EVENT_CONTENT_UPDATED: + // too spammy -- don't log these +// std::cerr << "Media event: MEDIA_EVENT_CONTENT_UPDATED " << std::endl; + break; + + case MEDIA_EVENT_TIME_DURATION_UPDATED: + // too spammy -- don't log these +// std::cerr << "Media event: MEDIA_EVENT_TIME_DURATION_UPDATED, time is " << self->getCurrentTime() << " of " << self->getDuration() << std::endl; + break; + + case MEDIA_EVENT_SIZE_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_SIZE_CHANGED " << std::endl; + break; + + case MEDIA_EVENT_CURSOR_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_CURSOR_CHANGED, new cursor is " << self->getCursorName() << std::endl; + break; + + case MEDIA_EVENT_NAVIGATE_BEGIN: + std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_BEGIN " << std::endl; + break; + + case MEDIA_EVENT_NAVIGATE_COMPLETE: + std::cerr << "Media event: MEDIA_EVENT_NAVIGATE_COMPLETE, result string is: " << self->getNavigateResultString() << std::endl; + break; + + case MEDIA_EVENT_PROGRESS_UPDATED: + std::cerr << "Media event: MEDIA_EVENT_PROGRESS_UPDATED, loading at " << self->getProgressPercent() << "%" << std::endl; + break; + + case MEDIA_EVENT_STATUS_TEXT_CHANGED: + std::cerr << "Media event: MEDIA_EVENT_STATUS_TEXT_CHANGED, new status text is: " << self->getStatusText() << std::endl; + break; + + case MEDIA_EVENT_LOCATION_CHANGED: + { + std::cerr << "Media event: MEDIA_EVENT_LOCATION_CHANGED, new uri is: " << self->getLocation() << std::endl; + mediaPanel* panel = findMediaPanel(self); + if(panel != NULL) + { + panel->mStartUrl = self->getLocation(); + if(panel == mSelectedPanel) + { + mUrlEdit->set_text(const_cast<char*>(panel->mStartUrl.c_str()) ); + } + } + } + break; + + case MEDIA_EVENT_CLICK_LINK_HREF: + std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, uri is " << self->getClickURL() << std::endl; + break; + + case MEDIA_EVENT_CLICK_LINK_NOFOLLOW: + std::cerr << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << std::endl; + break; + + case MEDIA_EVENT_PLUGIN_FAILED: + std::cerr << "Media event: MEDIA_EVENT_PLUGIN_FAILED" << std::endl; + break; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +static void gluiCallbackWrapper( int control_id ) +{ + if ( gApplication ) + gApplication->gluiCallback( control_id ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutReshape( int width, int height ) +{ + if ( gApplication ) + gApplication->reshape( width, height ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutDisplay() +{ + if ( gApplication ) + gApplication->display(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutIdle(int update_ms) +{ + GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + + if ( gApplication ) + gApplication->idle(); + +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutKeyboard( unsigned char key, int x, int y ) +{ + if ( gApplication ) + gApplication->keyboard( key ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMousePassive( int x, int y ) +{ + if ( gApplication ) + gApplication->mousePassive( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseMove( int x , int y ) +{ + if ( gApplication ) + gApplication->mouseMove( x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseButton( int button, int state, int x, int y ) +{ + if ( gApplication ) + gApplication->mouseButton( button, state, x, y ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int main( int argc, char* argv[] ) +{ + glutInit( &argc, argv ); + glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB ); + + const int app_window_x = 80; + const int app_window_y = 0; + const int app_window_width = 964; + const int app_window_height = 964; + + glutInitWindowPosition( app_window_x, app_window_y ); + glutInitWindowSize( app_window_width, app_window_height ); + + int app_window_handle = glutCreateWindow( "LLMediaPluginTest2" ); + + glutDisplayFunc( glutDisplay ); + + GLUI_Master.set_glutReshapeFunc( glutReshape ); + GLUI_Master.set_glutKeyboardFunc( glutKeyboard ); + GLUI_Master.set_glutMouseFunc( glutMouseButton ); + + glutPassiveMotionFunc( glutMousePassive ); + glutMotionFunc( glutMouseMove ); + + glutSetWindow( app_window_handle ); + + gApplication = new LLMediaPluginTest( app_window_handle, app_window_width, app_window_height ); + + // update at approximately 60hz + int update_ms = 1000 / 60; + + GLUI_Master.set_glutTimerFunc( update_ms, glutIdle, update_ms); + + glutMainLoop(); + + delete gApplication; +} diff --git a/indra/test_apps/llplugintest/llmediaplugintest.h b/indra/test_apps/llplugintest/llmediaplugintest.h new file mode 100644 index 0000000000..079b40ddc0 --- /dev/null +++ b/indra/test_apps/llplugintest/llmediaplugintest.h @@ -0,0 +1,200 @@ +/** + * @file LLMediaPluginTest2.cpp + * @brief Primary test application for LLMedia (Separate Process) Plugin system + * + * $LicenseInfo:firstyear=2008&license=viewergpl$ + * + * Copyright (c) 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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_MEDIA_PLUGIN_TEST_H +#define LL_MEDIA_PLUGIN_TEST_H + +#include <vector> +#include <string> +#include "llpluginclassmedia.h" + +// Forward declarations +class GLUI_Rotation; +class GLUI_Translation; +class GLUI_Listbox; +class GLUI_EditText; +class GLUI_StaticText; +class GLUI; +class GLUI_Button; + +//////////////////////////////////////////////////////////////////////////////// +// +struct mediaPanel +{ + public: + mediaPanel(); + ~mediaPanel(); + int mId; + std::string mStartUrl; + std::string mMimeType; + LLPluginClassMedia *mMediaSource; + int mMediaWidth; + int mMediaHeight; + int mTextureWidth; + int mTextureHeight; + double mTextureScaleX; + double mTextureScaleY; + GLuint mMediaTextureHandle; + GLuint mPickTextureHandle; + unsigned char* mPickTexturePixels; + bool mAppTextureCoordsOpenGL; + bool mReadyToRender; +}; + +//////////////////////////////////////////////////////////////////////////////// +// +class LLMediaPluginTest : public LLPluginClassMediaOwner +{ + public: + LLMediaPluginTest( int app_window, int window_width, int window_height ); + ~LLMediaPluginTest(); + + void reshape( int width, int height ); + void display(); + void idle(); + void gluiCallback( int control_id ); + void keyboard( int key ); + void mousePassive( int x, int y ); + void mouseButton( int button, int state, int x, int y ); + void mouseMove( int x, int y ); + + void bindTexture(GLuint texture, GLint row_length = 0, GLint alignment = 1); + bool checkGLError(char *name = "OpenGL"); + void drawGeometry( int panel ); + void startPanelHighlight( float red, float green, float blue, float line_width ); + void endPanelHighlight(); + enum { DrawTypePickTexture, DrawTypeMediaTexture }; + void draw( int draw_type ); + void windowPosToTexturePos( int window_x, int window_y, int& media_x, int& media_y, int& id ); + + void addMediaPanel( std::string url ); + void updateMediaPanel( mediaPanel* panel ); + void remMediaPanel( mediaPanel* panel ); + void replaceMediaPanel( mediaPanel* panel, std::string url ); + void getRandomMediaSize( int& width, int& height, std::string mime_type ); + void navigateToNewURI( std::string uri ); + void initUrlHistory( std::string uri ); + void selectPanelById( int id ); + void selectPanel( mediaPanel* panel ); + mediaPanel* findMediaPanel( LLPluginClassMedia* panel ); + void makePickTexture( int id, GLuint* texture_handle, unsigned char** texture_pixels ); + void makeChrome(); + void resetView(); + + void dumpPanelInfo(); + void updateStatusBar(); + + // Inherited from LLPluginClassMediaOwner + /*virtual*/ void handleMediaEvent(LLPluginClassMedia* self, LLPluginClassMediaOwner::EMediaEvent); + + private: + const int mVersionMajor; + const int mVersionMinor; + const int mVersionPatch; + const int mMaxPanels; + int mAppWindow; + int mWindowWidth; + int mWindowHeight; + int mCurMouseX; + int mCurMouseY; + unsigned char mPixelReadColor[ 3 ]; + bool mFuzzyMedia; + const std::string mHomeWebUrl; + + std::vector< mediaPanel* > mMediaPanels; + mediaPanel* mSelectedPanel; + std::string mimeTypeFromUrl( std::string& url ); + std::string pluginNameFromMimeType( std::string& mime_type ); + + GLUI_Rotation* mViewRotationCtrl; + GLUI_Translation* mViewScaleCtrl; + GLUI_Translation* mViewTranslationCtrl; + float mViewportAspect; + float mViewPos[ 3 ]; + float mViewRotation[ 16 ]; + + int mIdControlAddPanel; + int mIdControlRemPanel; + + std::vector< std::pair< std::string, std::string > > mBookmarks; + GLUI_Listbox* mBookmarkList; + int mIdBookmarks; + int mIdUrlEdit; + GLUI_EditText* mUrlEdit; + int mIdUrlInitHistoryEdit; + GLUI_EditText* mUrlInitHistoryEdit; + int mSelBookmark; + int mIdRandomPanelCount; + int mRandomPanelCount; + int mIdRandomBookmarks; + int mRandomBookmarks; + int mIdDisableTimeout; + int mDisableTimeout; + int mIdControlCrashPlugin; + int mIdControlHangPlugin; + int mIdControlExitApp; + + GLUI* mGluiMediaTimeControlWindow; + int mIdMediaTimeControlPlay; + int mIdMediaTimeControlLoop; + int mIdMediaTimeControlPause; + int mIdMediaTimeControlStop; + int mIdMediaTimeControlSeek; + int mIdMediaTimeControlVolume; + int mMediaTimeControlVolume; + int mIdMediaTimeControlSeekSeconds; + int mMediaTimeControlSeekSeconds; + int mIdMediaTimeControlRewind; + int mIdMediaTimeControlFastForward; + + GLUI* mGluiMediaBrowserControlWindow; + int mIdMediaBrowserControlBack; + GLUI_Button* mMediaBrowserControlBackButton; + int mIdMediaBrowserControlStop; + int mIdMediaBrowserControlForward; + GLUI_Button* mMediaBrowserControlForwardButton; + bool mGluiMediaTimeControlWindowFlag; + bool mGluiMediaBrowserControlWindowFlag; + bool mMediaBrowserControlBackButtonFlag; + bool mMediaBrowserControlForwardButtonFlag; + int mIdMediaBrowserControlHome; + int mIdMediaBrowserControlReload; + int mIdMediaBrowserControlClearCache; + int mIdMediaBrowserControlClearCookies; + int mIdMediaBrowserControlEnableCookies; + int mMediaBrowserControlEnableCookies; + + GLUI* mBottomGLUIWindow; + GLUI_StaticText* mStatusText; +}; + +#endif // LL_MEDIA_PLUGIN_TEST_H + |