svn merge -r 70819:70853 svn+ssh://svn/svn/linden/branches/urldispatcher-for-merge

meow-7.2.2
Tess Chu 2007-10-02 22:38:38 +00:00
parent 9763105427
commit cfd17f3322
13 changed files with 548 additions and 91 deletions

View File

@ -58,7 +58,8 @@ const S32 SPACES_PER_TAB = 4;
LLColor4 LLTextEditor::mLinkColor = LLColor4::blue;
void (* LLTextEditor::mURLcallback)(const char*) = NULL;
BOOL (* LLTextEditor::mSecondlifeURLcallback)(LLString) = NULL;
bool (* LLTextEditor::mSecondlifeURLcallback)(const std::string&) = NULL;
bool (* LLTextEditor::mSecondlifeURLcallbackRightClick)(const std::string&) = NULL;
///////////////////////////////////////////////////////////////////
//virtuals

View File

@ -194,9 +194,10 @@ public:
// Callbacks
static void setLinkColor(LLColor4 color) { mLinkColor = color; }
static void setURLCallbacks( void (*callback1) (const char* url),
BOOL (*callback2) (LLString url) )
{ mURLcallback = callback1; mSecondlifeURLcallback = callback2;}
static void setURLCallbacks(void (*callback1) (const char* 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);
@ -318,7 +319,8 @@ public:
LLKeywords mKeywords;
static LLColor4 mLinkColor;
static void (*mURLcallback) (const char* url);
static BOOL (*mSecondlifeURLcallback) (LLString url);
static bool (*mSecondlifeURLcallback) (const std::string& url);
static bool (*mSecondlifeURLcallbackRightClick) (const std::string& url);
protected:
LLWString mWText;
mutable LLString mUTF8Text;

View File

@ -0,0 +1,80 @@
/**
* @file llcommandhandler.cpp
* @brief Central registry for text-driven "commands", most of
* which manipulate user interface. For example, the command
* "agent (uuid) about" will open the UI for an avatar's profile.
*
* Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
* $License$
*/
#include "llviewerprecompiledheaders.h"
#include "llcommandhandler.h"
// system includes
#include <boost/tokenizer.hpp>
//---------------------------------------------------------------------------
// Underlying registry for command handlers, not directly accessible.
//---------------------------------------------------------------------------
class LLCommandHandlerRegistry
{
public:
static LLCommandHandlerRegistry& instance();
void add(const char* cmd, LLCommandHandler* handler);
bool dispatch(const std::string& cmd, const std::vector<std::string>& params);
private:
std::map<std::string, LLCommandHandler*> mMap;
};
// static
LLCommandHandlerRegistry& LLCommandHandlerRegistry::instance()
{
// Force this to be initialized on first call, because we're going
// to be adding items to the std::map before main() and we can't
// rely on a global being initialized in the right order.
static LLCommandHandlerRegistry instance;
return instance;
}
void LLCommandHandlerRegistry::add(const char* cmd, LLCommandHandler* handler)
{
mMap[cmd] = handler;
}
bool LLCommandHandlerRegistry::dispatch(const std::string& cmd,
const std::vector<std::string>& params)
{
std::map<std::string, LLCommandHandler*>::iterator it = mMap.find(cmd);
if (it == mMap.end()) return false;
LLCommandHandler* handler = it->second;
if (!handler) return false;
return handler->handle(params);
}
//---------------------------------------------------------------------------
// Automatic registration of commands, runs before main()
//---------------------------------------------------------------------------
LLCommandHandler::LLCommandHandler(const char* cmd)
{
LLCommandHandlerRegistry::instance().add(cmd, this);
}
LLCommandHandler::~LLCommandHandler()
{
// Don't care about unregistering these, all the handlers
// should be static objects.
}
//---------------------------------------------------------------------------
// Public interface
//---------------------------------------------------------------------------
// static
bool LLCommandDispatcher::dispatch(const std::string& cmd, const std::vector<std::string>& params)
{
return LLCommandHandlerRegistry::instance().dispatch(cmd, params);
}

View File

@ -0,0 +1,62 @@
/**
* @file llcommandhandler.h
* @brief Central registry for text-driven "commands", most of
* which manipulate user interface. For example, the command
* "agent (uuid) about" will open the UI for an avatar's profile.
*
* Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
* $License$
*/
#ifndef LLCOMMANDHANDLER_H
#define LLCOMMANDHANDLER_H
/* To implement a command "foo" that takes one parameter,
a UUID, do this:
class LLFooHandler : public LLCommandHandler
{
public:
// Inform the system you handle commands starting
// with "foo"
LLFooHandler() : LLCommandHandler("foo") { }
// Your code here
bool handle(const std::vector<std::string>& tokens)
{
if (tokens.size() < 1) return false;
LLUUID id( tokens[0] );
return doFoo(id);
}
};
// Creating the object registers with the dispatcher.
LLFooHandler gFooHandler;
*/
class LLCommandHandler
{
public:
LLCommandHandler(const char* command);
// Automatically registers object to get called when
// command is executed.
virtual ~LLCommandHandler();
virtual bool handle(const std::vector<std::string>& params) = 0;
// Execute the command with a provided (possibly empty)
// list of parameters.
// Return true if you did something, false if the parameters
// are invalid or on error.
};
class LLCommandDispatcher
{
public:
static bool dispatch(const std::string& cmd, const std::vector<std::string>& params);
// Execute a command registered via the above mechanism,
// passing string parameters.
// Returns true if command was found and executed correctly.
};
#endif

View File

@ -197,47 +197,3 @@ void LLFloaterMap::toggle(void*)
}
}
}
BOOL process_secondlife_url(LLString url)
{
S32 strpos, strpos2;
LLString slurlID = "slurl.com/secondlife/";
strpos = url.find(slurlID);
if (strpos < 0)
{
slurlID="secondlife://";
strpos = url.find(slurlID);
}
if (strpos >= 0)
{
LLString simname;
strpos+=slurlID.length();
strpos2=url.find("/",strpos);
if (strpos2 < strpos) strpos2=url.length();
simname="secondlife://" + url.substr(strpos,url.length() - strpos);
LLURLSimString::setString( simname );
LLURLSimString::parse();
// if there is a world map
if ( gFloaterWorldMap )
{
// mark where the destination is
gFloaterWorldMap->trackURL( LLURLSimString::sInstance.mSimName.c_str(),
LLURLSimString::sInstance.mX,
LLURLSimString::sInstance.mY,
LLURLSimString::sInstance.mZ );
// display map
LLFloaterWorldMap::show( NULL, TRUE );
};
return TRUE;
}
return FALSE;
}

View File

@ -46,6 +46,4 @@ protected:
extern LLFloaterMap *gFloaterMap;
BOOL process_secondlife_url(LLString url);
#endif // LL_LLFLOATERMAP_H

View File

@ -31,6 +31,7 @@
#include "lltextbox.h"
#include "llui.h"
#include "lluiconstants.h"
#include "llurlsimstring.h"
#include "llviewerbuild.h"
#include "llviewerimagelist.h"
#include "llviewermenu.h" // for handle_preferences()

View File

@ -107,6 +107,8 @@
#include "lltexturefetch.h"
#include "lltoolmgr.h"
#include "llui.h"
#include "llurldispatcher.h"
#include "llurlsimstring.h"
#include "llurlwhitelist.h"
#include "lluserauth.h"
#include "llviewerassetstorage.h"
@ -779,7 +781,7 @@ BOOL idle_startup()
//For HTML parsing in text boxes.
LLTextEditor::setLinkColor( gSavedSettings.getColor4("HTMLLinkColor") );
LLTextEditor::setURLCallbacks ( &LLWeb::loadURL, &process_secondlife_url );
LLTextEditor::setURLCallbacks ( &LLWeb::loadURL, &LLURLDispatcher::dispatch, &LLURLDispatcher::dispatch );
//-------------------------------------------------
// Handle startup progress screen
@ -2254,25 +2256,8 @@ BOOL idle_startup()
gAgentPilot.startPlayback();
}
// ok, if we've gotten this far and have a startup URL
if (LLURLSimString::sInstance.parse())
{
// kick off request for landmark to startup URL
if(gFloaterWorldMap)
{
LLVector3 pos = gAgent.getPositionAgent();
if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName()
|| LLURLSimString::sInstance.mX != llfloor(pos[VX])
|| LLURLSimString::sInstance.mY != llfloor(pos[VY]) )
{
LLFloaterWorldMap::show(NULL, TRUE);
gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName,
LLURLSimString::sInstance.mX,
LLURLSimString::sInstance.mY,
LLURLSimString::sInstance.mZ);
}
}
}
// If we've got a startup URL, dispatch it
LLStartUp::dispatchURL();
// Clean up the userauth stuff.
if (gUserAuthp)
@ -3664,13 +3649,6 @@ void release_start_screen()
gStartImageGL = NULL;
}
// static
bool LLStartUp::canGoFullscreen()
{
return LLStartUp::getStartupState() >= STATE_WORLD_INIT;
}
// static
void LLStartUp::setStartupState( S32 state )
{
@ -3692,3 +3670,40 @@ void reset_login()
if ( gFloaterMap )
gFloaterMap->setVisible( FALSE );
}
//---------------------------------------------------------------------------
std::string LLStartUp::sSLURLCommand;
bool LLStartUp::canGoFullscreen()
{
return gStartupState >= STATE_WORLD_INIT;
}
bool LLStartUp::dispatchURL()
{
// ok, if we've gotten this far and have a startup URL
if (!sSLURLCommand.empty())
{
LLURLDispatcher::dispatch(sSLURLCommand);
}
else if (LLURLSimString::parse())
{
// If we started with a location, but we're already
// at that location, don't pop dialogs open.
LLVector3 pos = gAgent.getPositionAgent();
F32 dx = pos.mV[VX] - (F32)LLURLSimString::sInstance.mX;
F32 dy = pos.mV[VY] - (F32)LLURLSimString::sInstance.mY;
const F32 SLOP = 2.f; // meters
if( LLURLSimString::sInstance.mSimName != gAgent.getRegion()->getName()
|| (dx*dx > SLOP*SLOP)
|| (dy*dy > SLOP*SLOP) )
{
std::string url = LLURLSimString::getURL();
LLURLDispatcher::dispatch(url);
}
return true;
}
return false;
}

View File

@ -64,6 +64,14 @@ public:
static void setStartupState( S32 state );
static S32 getStartupState() { return gStartupState; };
static bool dispatchURL();
// if we have a SLURL or sim string ("Ahern/123/45") that started
// the viewer, dispatch it
static std::string sSLURLCommand;
// *HACK: On startup, if we were passed a secondlife://app/do/foo
// command URL, store it for later processing.
protected:
static S32 gStartupState; // Do not set directly, use LLStartup::setStartupState
};

View File

@ -0,0 +1,303 @@
/**
* @file llurldispatcher.cpp
* @brief Central registry for all URL handlers
*
* Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
* $License$
*/
#include "llviewerprecompiledheaders.h"
#include "llurldispatcher.h"
// viewer includes
#include "llagent.h" // teleportViaLocation()
#include "llcommandhandler.h"
// *FIX: code in merge sl-search-8
//#include "llfloaterurldisplay.h"
#include "llfloaterdirectory.h"
#include "llfloaterhtmlhelp.h"
#include "llfloaterworldmap.h"
#include "llpanellogin.h"
#include "llstartup.h" // gStartupState
#include "llurlsimstring.h"
#include "llviewerwindow.h" // alertXml()
#include "llworldmap.h"
// library includes
#include "llsd.h"
// system includes
#include <boost/tokenizer.hpp>
const std::string SLURL_SL_HELP_PREFIX = "secondlife://app.";
const std::string SLURL_SL_PREFIX = "sl://";
const std::string SLURL_SECONDLIFE_PREFIX = "secondlife://";
const std::string SLURL_SLURL_PREFIX = "http://slurl.com/secondlife/";
const std::string SLURL_APP_TOKEN = "app/";
class LLURLDispatcherImpl
{
public:
static bool isSLURL(const std::string& url);
static bool isSLURLCommand(const std::string& url);
static bool dispatch(const std::string& url);
// returns true if handled
static bool dispatchRightClick(const std::string& url);
private:
static bool dispatchCore(const std::string& url, bool right_mouse);
// handles both left and right click
static bool dispatchHelp(const std::string& url, BOOL right_mouse);
// Handles sl://app.floater.html.help by showing Help floater.
// Returns true if handled.
static bool dispatchApp(const std::string& url, BOOL right_mouse);
// Handles secondlife://app/agent/<agent_id>/about and similar
// by showing panel in Search floater.
// Returns true if handled.
static bool dispatchRegion(const std::string& url, BOOL right_mouse);
// handles secondlife://Ahern/123/45/67/
// Returns true if handled.
// *FIX: code in merge sl-search-8
// static void regionHandleCallback(U64 handle, const std::string& url,
// const LLUUID& snapshot_id, bool teleport);
// // Called by LLWorldMap when a region name has been resolved to a
// // location in-world, used by places-panel display.
static bool matchPrefix(const std::string& url, const std::string& prefix);
static std::string stripProtocol(const std::string& url);
friend class LLTeleportHandler;
};
// static
bool LLURLDispatcherImpl::isSLURL(const std::string& url)
{
if (matchPrefix(url, SLURL_SL_HELP_PREFIX)) return true;
if (matchPrefix(url, SLURL_SL_PREFIX)) return true;
if (matchPrefix(url, SLURL_SECONDLIFE_PREFIX)) return true;
if (matchPrefix(url, SLURL_SLURL_PREFIX)) return true;
return false;
}
// static
bool LLURLDispatcherImpl::isSLURLCommand(const std::string& url)
{
if (matchPrefix(url, SLURL_SL_PREFIX + SLURL_APP_TOKEN)
|| matchPrefix(url, SLURL_SECONDLIFE_PREFIX + SLURL_APP_TOKEN)
|| matchPrefix(url, SLURL_SLURL_PREFIX + SLURL_APP_TOKEN) )
{
return true;
}
return false;
}
// static
bool LLURLDispatcherImpl::dispatchCore(const std::string& url, bool right_mouse)
{
if (url.empty()) return false;
if (dispatchHelp(url, right_mouse)) return true;
if (dispatchApp(url, right_mouse)) return true;
if (dispatchRegion(url, right_mouse)) return true;
return false;
}
// static
bool LLURLDispatcherImpl::dispatch(const std::string& url)
{
llinfos << "url: " << url << llendl;
return dispatchCore(url, false); // not right click
}
// static
bool LLURLDispatcherImpl::dispatchRightClick(const std::string& url)
{
llinfos << "url: " << url << llendl;
return dispatchCore(url, true); // yes right click
}
// static
bool LLURLDispatcherImpl::dispatchHelp(const std::string& url, BOOL right_mouse)
{
if (matchPrefix(url, SLURL_SL_HELP_PREFIX))
{
gViewerHtmlHelp.show();
return true;
}
return false;
}
// static
bool LLURLDispatcherImpl::dispatchApp(const std::string& url, BOOL right_mouse)
{
if (!isSLURL(url))
{
return false;
}
std::string s = stripProtocol(url);
// At this point, "secondlife://app/foo/bar/baz/" should be left
// as: "app/foo/bar/baz/"
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
tokenizer tokens(s, sep);
tokenizer::iterator it = tokens.begin();
tokenizer::iterator end = tokens.end();
// Build parameter list suitable for LLDispatcher dispatch
if (it == end) return false;
if (*it != "app") return false;
++it;
if (it == end) return false;
std::string cmd = *it;
++it;
std::vector<std::string> params;
for ( ; it != end; ++it)
{
params.push_back(*it);
}
bool handled = LLCommandDispatcher::dispatch(cmd, params);
if (handled) return true;
// Inform the user we can't handle this
std::map<std::string, std::string> args;
args["[SLURL]"] = url;
gViewerWindow->alertXml("BadURL", args);
return false;
}
// static
bool LLURLDispatcherImpl::dispatchRegion(const std::string& url, BOOL right_mouse)
{
if (!isSLURL(url))
{
return false;
}
// Before we're logged in, need to update the startup screen
// to tell the user where they are going.
if (LLStartUp::getStartupState() < STATE_CLEANUP)
{
// Parse it and stash in globals, it will be dispatched in
// STATE_CLEANUP.
LLURLSimString::setString(url);
// We're at the login screen, so make sure user can see
// the login location box to know where they are going.
LLPanelLogin::refreshLocation( true );
return true;
}
std::string sim_string = stripProtocol(url);
std::string region_name;
S32 x = 128;
S32 y = 128;
S32 z = 0;
LLURLSimString::parse(sim_string, &region_name, &x, &y, &z);
if (gFloaterWorldMap)
{
llinfos << "Opening map to " << region_name << llendl;
gFloaterWorldMap->trackURL( region_name.c_str(), x, y, z );
LLFloaterWorldMap::show(NULL, TRUE);
}
return true;
}
// static
bool LLURLDispatcherImpl::matchPrefix(const std::string& url, const std::string& prefix)
{
std::string test_prefix = url.substr(0, prefix.length());
LLString::toLower(test_prefix);
return test_prefix == prefix;
}
// static
std::string LLURLDispatcherImpl::stripProtocol(const std::string& url)
{
std::string stripped = url;
if (matchPrefix(stripped, SLURL_SL_HELP_PREFIX))
{
stripped.erase(0, SLURL_SL_HELP_PREFIX.length());
}
else if (matchPrefix(stripped, SLURL_SL_PREFIX))
{
stripped.erase(0, SLURL_SL_PREFIX.length());
}
else if (matchPrefix(stripped, SLURL_SECONDLIFE_PREFIX))
{
stripped.erase(0, SLURL_SECONDLIFE_PREFIX.length());
}
else if (matchPrefix(stripped, SLURL_SLURL_PREFIX))
{
stripped.erase(0, SLURL_SLURL_PREFIX.length());
}
return stripped;
}
// *FIX: code in merge sl-search-8
//
////---------------------------------------------------------------------------
//// Teleportation links are handled here because they are tightly coupled
//// to URL parsing and sim-fragment parsing
//class LLTeleportHandler : public LLCommandHandler
//{
//public:
// LLTeleportHandler() : LLCommandHandler("teleport") { }
// bool handle(const std::vector<std::string>& tokens)
// {
// // construct a "normal" SLURL, resolve the region to
// // a global position, and teleport to it
// if (tokens.size() < 1) return false;
//
// // Region names may be %20 escaped.
// std::string region_name = LLURLSimString::unescapeRegionName(tokens[0]);
//
// // build secondlife://De%20Haro/123/45/67 for use in callback
// std::string url = SLURL_SECONDLIFE_PREFIX;
// for (size_t i = 0; i < tokens.size(); ++i)
// {
// url += tokens[i] + "/";
// }
// gWorldMap->sendNamedRegionRequest(region_name,
// LLURLDispatcherImpl::regionHandleCallback,
// url,
// true); // teleport
// return true;
// }
//};
//LLTeleportHandler gTeleportHandler;
//---------------------------------------------------------------------------
// static
bool LLURLDispatcher::isSLURL(const std::string& url)
{
return LLURLDispatcherImpl::isSLURL(url);
}
// static
bool LLURLDispatcher::isSLURLCommand(const std::string& url)
{
return LLURLDispatcherImpl::isSLURLCommand(url);
}
// static
bool LLURLDispatcher::dispatch(const std::string& url)
{
return LLURLDispatcherImpl::dispatch(url);
}
// static
bool LLURLDispatcher::dispatchRightClick(const std::string& url)
{
return LLURLDispatcherImpl::dispatchRightClick(url);
}

View File

@ -0,0 +1,32 @@
/**
* @file llurldispatcher.h
* @brief Central registry for all SL URL handlers
*
* Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
* $License$
*/
#ifndef LLURLDISPATCHER_H
#define LLURLDISPATCHER_H
class LLURLDispatcher
{
public:
static bool isSLURL(const std::string& url);
// Is this any sort of secondlife:// or sl:// URL?
static bool isSLURLCommand(const std::string& url);
// Is this a special secondlife://app/ URL?
static bool dispatch(const std::string& url);
// At startup time and on clicks in internal web browsers,
// teleport, open map, or run requested command.
// Handles:
// secondlife://RegionName/123/45/67/
// secondlife://app/agent/3d6181b0-6a4b-97ef-18d8-722652995cf1/show
// sl://app/foo/bar
// Returns true if someone handled the URL.
static bool dispatchRightClick(const std::string& url);
};
#endif

View File

@ -83,7 +83,6 @@
#include "llfloatergroups.h"
#include "llfloaterhtml.h"
#include "llfloaterhtmlhelp.h"
#include "llfloaterhtmlfind.h"
#include "llfloaterinspect.h"
#include "llfloaterlagmeter.h"
#include "llfloaterland.h"
@ -340,6 +339,7 @@ void toggle_show_xui_names(void *);
BOOL check_show_xui_names(void *);
// Debug UI
void handle_slurl_test(void*);
void handle_save_to_xml(void*);
void handle_load_from_xml(void*);
@ -976,6 +976,7 @@ extern BOOL gVectorizePerfTest;
void init_debug_ui_menu(LLMenuGL* menu)
{
menu->append(new LLMenuItemCallGL("SLURL Test", &handle_slurl_test));
menu->append(new LLMenuItemCallGL("Editable UI", &edit_ui));
menu->append(new LLMenuItemToggleGL("Async Keystrokes", &gHandleKeysAsync));
menu->append(new LLMenuItemCallGL( "Dump SelectMgr", &dump_select_mgr));
@ -7282,6 +7283,11 @@ void handle_load_from_xml(void*)
}
}
void handle_slurl_test(void*)
{
LLFloaterHtml::getInstance()->show("http://user.lindenlab.com/~james/slurl.html", "SLURL Test");
}
void handle_rebake_textures(void*)
{
LLVOAvatar* avatar = gAgent.getAvatarObject();

View File

@ -136,6 +136,7 @@
#include "lltoolview.h"
#include "llvieweruictrlfactory.h"
#include "lluploaddialog.h"
#include "llurldispatcher.h" // SLURL from other app instance
#include "llviewercamera.h"
#include "llviewergesture.h"
#include "llviewerimagelist.h"
@ -1394,22 +1395,14 @@ void LLViewerWindow::handleWindowUnblock(LLWindow *window)
void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data)
{
const S32 SLURL_MESSAGE_TYPE = 0;
switch (data_type)
{
case 0:
case SLURL_MESSAGE_TYPE:
// received URL
if (LLURLSimString::unpack_data(data))
std::string url = (const char*)data;
if (LLURLDispatcher::dispatch(url))
{
if(gFloaterWorldMap)
{
gFloaterWorldMap->trackURL(LLURLSimString::sInstance.mSimName,
LLURLSimString::sInstance.mX,
LLURLSimString::sInstance.mY,
LLURLSimString::sInstance.mZ);
LLFloaterWorldMap::show(NULL, TRUE);
}
// bring window to foreground, as it has just been "launched" from a URL
mWindow->bringToFront();
}