summaryrefslogtreecommitdiff
path: root/indra/newview/llviewerwindow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llviewerwindow.cpp')
-rw-r--r--indra/newview/llviewerwindow.cpp4816
1 files changed, 4816 insertions, 0 deletions
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
new file mode 100644
index 0000000000..3836b2d1d7
--- /dev/null
+++ b/indra/newview/llviewerwindow.cpp
@@ -0,0 +1,4816 @@
+/**
+ * @file llviewerwindow.cpp
+ * @brief Implementation of the LLViewerWindow class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+// system library includes
+#include <stdio.h>
+#include <iostream>
+#include <fstream>
+
+#include "llviewerwindow.h"
+#include "llviewquery.h"
+#include "llxmltree.h"
+#include "llviewercamera.h"
+//#include "imdebug.h"
+
+#ifdef SABINRIG
+#include "cbw.h"
+#endif //SABINRIG
+
+//
+// TODO: Many of these includes are unnecessary. Remove them.
+//
+
+// linden library includes
+#include "audioengine.h" // mute on minimize
+#include "indra_constants.h"
+#include "linked_lists.h"
+#include "llassetstorage.h"
+#include "llfontgl.h"
+#include "llmediaengine.h" // mute on minimize
+#include "llrect.h"
+#include "llsky.h"
+#include "llstring.h"
+#include "llui.h"
+#include "lluuid.h"
+#include "llview.h"
+#include "llxfermanager.h"
+#include "message.h"
+#include "object_flags.h"
+#include "text_out.h"
+#include "lltimer.h"
+#include "timing.h"
+#include "llviewermenu.h"
+
+// newview includes
+#include "llagent.h"
+#include "llalertdialog.h"
+#include "llbox.h"
+#include "llcameraview.h"
+#include "llchatbar.h"
+#include "llconsole.h"
+#include "llviewercontrol.h"
+#include "llcylinder.h"
+#include "lldebugview.h"
+#include "lldir.h"
+#include "lldrawable.h"
+#include "lldrawpoolalpha.h"
+#include "lldrawpoolbump.h"
+#include "lldrawpoolwater.h"
+#include "llmaniptranslate.h"
+#include "llface.h"
+#include "llfeaturemanager.h"
+#include "llfilepicker.h"
+#include "llfloater.h"
+#include "llfloaterbuildoptions.h"
+#include "llfloaterchat.h"
+#include "llfloatercustomize.h"
+#include "llfloatereditui.h" // HACK JAMESDEBUG for ui editor
+#include "llfloaterland.h"
+#include "llfloatermap.h"
+#include "llfloatermute.h"
+#include "llfloaternamedesc.h"
+#include "llfloatersnapshot.h"
+#include "llfloatertools.h"
+#include "llfloaterworldmap.h"
+#include "llfocusmgr.h"
+#include "llframestatview.h"
+#include "llgesturemgr.h"
+#include "llglheaders.h"
+#include "llhippo.h"
+#include "llhoverview.h"
+#include "llhudmanager.h"
+#include "llhudview.h"
+#include "llimagebmp.h"
+#include "llimagej2c.h"
+#include "llinventoryview.h"
+#include "llkeyboard.h"
+#include "lllineeditor.h"
+#include "llmenugl.h"
+#include "llmodaldialog.h"
+#include "llmorphview.h"
+#include "llmoveview.h"
+#include "llnotify.h"
+#include "lloverlaybar.h"
+#include "llpreviewtexture.h"
+#include "llprogressview.h"
+#include "llresmgr.h"
+#include "llrootview.h"
+#include "llselectmgr.h"
+#include "llsphere.h"
+#include "llstartup.h"
+#include "llstatusbar.h"
+#include "llstatview.h"
+#include "llsurface.h"
+#include "llsurfacepatch.h"
+#include "llimview.h"
+#include "lltexlayer.h"
+#include "lltextbox.h"
+#include "lltextureview.h"
+#include "lltool.h"
+#include "lltoolbar.h"
+#include "lltoolcomp.h"
+#include "lltooldraganddrop.h"
+#include "lltoolface.h"
+#include "lltoolfocus.h"
+#include "lltoolgrab.h"
+#include "lltoolmgr.h"
+#include "lltoolmorph.h"
+#include "lltoolpie.h"
+#include "lltoolplacer.h"
+#include "lltoolselect.h"
+#include "lltoolselectland.h"
+#include "lltoolview.h"
+#include "llvieweruictrlfactory.h"
+#include "lluploaddialog.h"
+#include "llviewercamera.h"
+#include "llviewergesture.h"
+#include "llviewerimagelist.h"
+#include "llviewerinventory.h"
+#include "llviewerkeyboard.h"
+#include "llviewermenu.h"
+#include "llviewermessage.h"
+#include "llviewerobjectlist.h"
+#include "llviewerparcelmgr.h"
+#include "llviewerregion.h"
+#include "llviewerstats.h"
+#include "llvoavatar.h"
+#include "llvovolume.h"
+#include "llworld.h"
+#include "llworldmapview.h"
+#include "moviemaker.h"
+#include "pipeline.h"
+#include "viewer.h"
+
+#if LL_WINDOWS
+#include "llwindebug.h"
+#include <tchar.h> // For Unicode conversion methods
+#endif
+
+//
+// Globals
+//
+
+LLBottomPanel* gBottomPanel = NULL;
+
+extern BOOL gDebugClicks;
+extern BOOL gDisplaySwapBuffers;
+extern S32 gJamesInt;
+
+LLViewerWindow *gViewerWindow = NULL;
+LLVelocityBar *gVelocityBar = NULL;
+
+LLVector3d gLastHitPosGlobal;
+LLVector3d gLastHitObjectOffset;
+LLUUID gLastHitObjectID;
+S32 gLastHitObjectFace = -1;
+BOOL gLastHitLand = FALSE;
+F32 gLastHitUCoord;
+F32 gLastHitVCoord;
+
+
+LLVector3d gLastHitNonFloraPosGlobal;
+LLVector3d gLastHitNonFloraObjectOffset;
+LLUUID gLastHitNonFloraObjectID;
+S32 gLastHitNonFloraObjectFace = -1;
+BOOL gLastHitParcelWall = FALSE;
+
+S32 gLastHitUIElement = 0;
+LLHUDIcon* gLastHitHUDIcon = NULL;
+
+BOOL gDebugSelect = FALSE;
+U8 gLastPickAlpha = 255;
+BOOL gUseGLPick = FALSE;
+
+// On the next pick pass (whenever that happens)
+// should we try to pick individual faces?
+// Cleared to FALSE every time a pick happens.
+BOOL gPickFaces = FALSE;
+
+LLFrameTimer gMouseIdleTimer;
+LLFrameTimer gAwayTimer;
+LLFrameTimer gAwayTriggerTimer;
+LLFrameTimer gAlphaFadeTimer;
+
+BOOL gShowOverlayTitle = FALSE;
+BOOL gPickTransparent = TRUE;
+
+BOOL gDebugFastUIRender = FALSE;
+
+BOOL gbCapturing = FALSE;
+MovieMaker gMovieMaker;
+
+S32 CHAT_BAR_HEIGHT = 28;
+S32 OVERLAY_BAR_HEIGHT = 20;
+
+const U8 NO_FACE = 255;
+BOOL gQuietSnapshot = FALSE;
+
+const F32 MIN_AFK_TIME = 2.f; // minimum time after setting away state before coming back
+const F32 MAX_FAST_FRAME_TIME = 0.5f;
+const F32 FAST_FRAME_INCREMENT = 0.1f;
+
+const S32 PICK_HALF_WIDTH = 5;
+const S32 PICK_DIAMETER = 2 * PICK_HALF_WIDTH+1;
+
+const F32 MIN_DISPLAY_SCALE = 0.85f;
+
+#ifdef SABINRIG
+/// ALL RIG STUFF
+bool rigControl = false;
+bool voltDisplay = true;
+bool nominalX = false;
+bool nominalY = false;
+static F32 nomerX = 0.0f;
+static F32 nomerY = 0.0f;
+const BOARD_NUM = 0; // rig stuff!
+const ADRANGE = BIP10VOLTS; // rig stuff!
+static unsigned short DataVal; // rig stuff!
+static F32 oldValueX = 0;
+static F32 newValueX = 50;
+static F32 oldValueY = 0;
+static F32 newValueY = 50;
+static S32 mouseX = 50;
+static S32 mouseY = 50;
+static float VoltageX = 50; // rig stuff!
+static float VoltageY = 50; // rig stuff!
+static float nVoltX = 0;
+static float nVoltY = 0;
+static F32 temp1 = 50.f;
+static F32 temp2 = 20.f;
+LLCoordGL new_gl;
+#endif //SABINRIG
+
+char LLViewerWindow::sSnapshotBaseName[LL_MAX_PATH];
+char LLViewerWindow::sSnapshotDir[LL_MAX_PATH];
+
+char LLViewerWindow::sMovieBaseName[LL_MAX_PATH];
+
+extern void toggle_debug_menus(void*);
+
+#ifdef SABINRIG
+// static
+void LLViewerWindow::printFeedback()
+{
+ if(rigControl == true)
+ {
+ cbAIn (BOARD_NUM, 0, ADRANGE, &DataVal);
+ cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageX); //Convert raw to voltage for X-axis
+ cbAIn (BOARD_NUM, 1, ADRANGE, &DataVal);
+ cbToEngUnits (BOARD_NUM,ADRANGE,DataVal,&VoltageY); //Convert raw to voltage for Y-axis
+ if(voltDisplay == true)
+ {
+ llinfos << "Current Voltages - X:" << VoltageX << " Y:" << VoltageY << llendl; //Display voltage
+ }
+
+ if(nVoltX == 0)
+ {
+ nVoltX = VoltageX;
+ nVoltY = VoltageY; //First time setup of nominal values.
+ }
+
+ newValueX = VoltageX;
+ newValueY = VoltageY; //Take in current voltage and set to a separate value for adjustment.
+
+ mouseX = mCurrentMousePoint.mX;
+ mouseY = mCurrentMousePoint.mY; //Take in current cursor position and set to separate value for adjustment.
+
+ if( abs(newValueX - nVoltX) > nomerX )
+ {
+ if( (newValueX - oldValueX) < 0)
+ {
+ mouseX += (S32)( ((newValueX - oldValueX)*.5)) * -temp;
+ }
+ else
+ {
+ mouseX += (S32)( ((newValueX - oldValueX)*.5) * temp1);
+ }
+ }
+ else
+ {
+ mouseX = getWindowWidth() / 2;
+ }
+ if( abs(newValueY - nVoltY) > nomerY )
+ {
+ if( (newValueY - oldValueY) < 0)
+ {
+ mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * -temp2);
+ }
+ else
+ {
+ mouseY += (S32)( ((newValueY - oldValueY)*(newValueY - oldValueY)) * temp2);
+ }
+ }
+ else
+ {
+ mouseY = getWindowHeight() / 2;
+ }
+ //mouseX += (S32)( (newValueX - nVoltX) * temp1 + 0.5 );
+ // (newValueX - oldValueX) = difference between current position and nominal position
+ // * temp1 = the amplification of the number that sets sensitivity
+ // + 0.5 = fixes rounding errors
+
+
+ //mouseY += (S32)( (newValueY - nVoltY) * temp2 + 0.5 ); //Algorithm to adjust voltage for mouse adjustment.
+
+ oldValueX = newValueX;
+ oldValueY = newValueY;
+
+ new_gl.mX = mouseX;
+ new_gl.mY = mouseY; //Setup final coordinate to move mouse to.
+
+ setCursorPosition(new_gl); //Set final cursor position
+ }
+}
+#endif //SABINRIG
+
+
+//
+// LLViewerWindow
+//
+
+BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse down at " << x << "," << y << llendl;
+ }
+
+ mLeftMouseDown = TRUE;
+
+ // Make sure we get a coresponding mouseup event, even if the mouse leaves the window
+ mWindow->captureMouse();
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mousedown
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mousedown
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+ }
+
+ if (gToolMgr)
+ {
+ // Don't let the user move the mouse out of the window until mouse up.
+ if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(TRUE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleMouseDown(local_x, local_y, mask);
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleMouseDown(local_x, local_y, mask)) return TRUE;
+ }
+
+ // Give the UI views a chance to process the click
+ if( mRootView->handleMouseDown(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down not handled by view" << llendl;
+ }
+
+ if (gDisconnected)
+ {
+ return FALSE;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleMouseDown( x, y, mask ) )
+ {
+ // This is necessary to force clicks in the world to cause edit
+ // boxes that might have keyboard focus to relinquish it, and hence
+ // cause a commit to update their value. JC
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse double-click at " << x << "," << y << llendl;
+ }
+
+ mLeftMouseDown = TRUE;
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleDoubleClick(local_x, local_y, mask);
+ }
+
+ // Check for hit on UI.
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleDoubleClick(local_x, local_y, mask)) return TRUE;
+ }
+
+ if (mRootView->handleDoubleClick(x, y, mask))
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Down not handled by view" << llendl;
+ }
+
+ // Why is this here? JC 9/3/2002
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleDoubleClick( x, y, mask ) )
+ {
+ return TRUE;
+ }
+ }
+
+ // if we got this far and nothing handled a double click, pass a normal mouse down
+ return handleMouseDown(window, pos, mask);
+}
+
+BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow left mouse up" << llendl;
+ }
+
+ mLeftMouseDown = FALSE;
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mouseup
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mouseup
+ if (gHoverView) gHoverView->cancelHover();
+
+ BOOL handled = FALSE;
+
+ mWindow->releaseMouse();
+
+ LLTool *tool = NULL;
+ if (gToolMgr)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if( tool->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(FALSE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Left Mouse Up handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ return mouse_captor->handleMouseUp(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleMouseUp(local_x, local_y, mask);
+ }
+
+ if( !handled )
+ {
+ handled = mRootView->handleMouseUp(x, y, mask);
+ }
+
+ if (LLView::sDebugMouseHandling)
+ {
+ if (handled)
+ {
+ llinfos << "Left Mouse Up" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ else
+ {
+ llinfos << "Left Mouse Up not handled by view" << llendl;
+ }
+ }
+
+ if( !handled )
+ {
+ if (tool)
+ {
+ handled = tool->handleMouseUp(x, y, mask);
+ }
+ }
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+
+BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow right mouse down at " << x << "," << y << llendl;
+ }
+
+ mRightMouseDown = TRUE;
+
+ // Make sure we get a coresponding mouseup event, even if the mouse leaves the window
+ mWindow->captureMouse();
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mousedown
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+ }
+
+ if (gToolMgr)
+ {
+ // Don't let the user move the mouse out of the window until mouse up.
+ if( gToolMgr->getCurrentTool(mask)->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(TRUE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return mouse_captor->handleRightMouseDown(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ if (top_view->pointInView(local_x, local_y) && top_view->handleRightMouseDown(local_x, local_y, mask)) return TRUE;
+ }
+
+ if( mRootView->handleRightMouseDown(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Down not handled by view" << llendl;
+ }
+
+ if (gToolMgr)
+ {
+ if(gToolMgr->getCurrentTool(mask)->handleRightMouseDown( x, y, mask ) )
+ {
+ // This is necessary to force clicks in the world to cause edit
+ // boxes that might have keyboard focus to relinquish it, and hence
+ // cause a commit to update their value. JC
+ gFocusMgr.setKeyboardFocus(NULL, NULL);
+ return TRUE;
+ }
+ }
+
+ //FIXME: this should be rolled into the composite tool logic, not hardcoded at the top level
+ if (gToolPie && (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgent.getCameraMode()) )
+ {
+ // If the current tool didn't process the click, we should show
+ // the pie menu. This can be done by passing the event to the pie
+ // menu tool.
+ gToolPie->handleRightMouseDown(x, y, mask);
+ // show_context_menu( x, y, mask );
+ }
+
+ return TRUE;
+}
+
+BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ // Don't care about caps lock for mouse events.
+ if (gDebugClicks)
+ {
+ llinfos << "ViewerWindow right mouse up" << llendl;
+ }
+
+ mRightMouseDown = FALSE;
+
+ // Indicate mouse was active
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips on mouseup
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on mouseup
+ if (gHoverView) gHoverView->cancelHover();
+
+ BOOL handled = FALSE;
+
+ mWindow->releaseMouse();
+
+ LLTool *tool = NULL;
+ if (gToolMgr)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if( tool->clipMouseWhenDown() )
+ {
+ mWindow->setMouseClipping(FALSE);
+ }
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Right Mouse Up handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return mouse_captor->handleRightMouseUp(local_x, local_y, mask);
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleRightMouseUp(local_x, local_y, mask);
+ }
+
+ if( !handled )
+ {
+ handled = mRootView->handleRightMouseUp(x, y, mask);
+ }
+
+ if (LLView::sDebugMouseHandling)
+ {
+ if (handled)
+ {
+ llinfos << "Right Mouse Up" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ else
+ {
+ llinfos << "Right Mouse Up not handled by view" << llendl;
+ }
+ }
+
+ if( !handled )
+ {
+ if (tool)
+ {
+ handled = tool->handleRightMouseUp(x, y, mask);
+ }
+ }
+
+ // Always handled as far as the OS is concerned.
+ return TRUE;
+}
+
+
+void LLViewerWindow::handleMouseMove(LLWindow *window, LLCoordGL pos, MASK mask)
+{
+ S32 x = pos.mX;
+ S32 y = pos.mY;
+
+ x = llround((F32)x / mDisplayScale.mV[VX]);
+ y = llround((F32)y / mDisplayScale.mV[VY]);
+
+ mMouseInWindow = TRUE;
+
+ // Save mouse point for access during idle() and display()
+
+ LLCoordGL prev_saved_mouse_point = mCurrentMousePoint;
+ LLCoordGL mouse_point(x, y);
+ saveLastMouse(mouse_point);
+ BOOL mouse_actually_moved = (prev_saved_mouse_point.mX != mCurrentMousePoint.mX) || (prev_saved_mouse_point.mY != mCurrentMousePoint.mY);
+
+ gMouseIdleTimer.reset();
+
+ mWindow->showCursorFromMouseMove();
+
+ if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
+ {
+ gAgent.clearAFK();
+ }
+
+ if(mToolTip && mouse_actually_moved)
+ {
+ mToolTipBlocked = FALSE; // Blocking starts on keyboard events and (only) ends here.
+ if( mToolTip->getVisible() && !mToolTipStickyRect.pointInRect( x, y ) )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+ }
+
+ // Activate the hover picker on mouse move.
+ if (gHoverView)
+ {
+ gHoverView->setTyping(FALSE);
+ }
+}
+
+void LLViewerWindow::handleMouseLeave(LLWindow *window)
+{
+ // Note: we won't get this if we have captured the mouse.
+ llassert( gFocusMgr.getMouseCapture() == NULL );
+ mMouseInWindow = FALSE;
+ if (mToolTip)
+ {
+ mToolTip->setVisible( FALSE );
+ }
+}
+
+BOOL LLViewerWindow::handleCloseRequest(LLWindow *window)
+{
+ // User has indicated they want to close, but we may need to ask
+ // about modified documents.
+ app_request_quit();
+ // Don't quit immediately
+ return FALSE;
+}
+
+void LLViewerWindow::handleQuit(LLWindow *window)
+{
+ app_force_quit(NULL);
+}
+
+void LLViewerWindow::handleResize(LLWindow *window, S32 width, S32 height)
+{
+ reshape(width, height);
+}
+
+// The top-level window has gained focus (e.g. via ALT-TAB)
+void LLViewerWindow::handleFocus(LLWindow *window)
+{
+ gFocusMgr.setAppHasFocus(TRUE);
+ LLModalDialog::onAppFocusGained();
+ if (gToolMgr)
+ {
+ gToolMgr->onAppFocusGained();
+ }
+
+ gShowTextEditCursor = TRUE;
+
+ // See if we're coming in with modifier keys held down
+ if (gKeyboard)
+ {
+ gKeyboard->resetMaskKeys();
+ }
+}
+
+// The top-level window has lost focus (e.g. via ALT-TAB)
+void LLViewerWindow::handleFocusLost(LLWindow *window)
+{
+ gFocusMgr.setAppHasFocus(FALSE);
+ //LLModalDialog::onAppFocusLost();
+ if( gToolMgr )
+ {
+ gToolMgr->onAppFocusLost();
+ }
+ gFocusMgr.setMouseCapture( NULL, NULL );
+
+ // restore mouse cursor
+ gViewerWindow->showCursor();
+ gViewerWindow->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, NULL );
+ gShowTextEditCursor = FALSE;
+
+ // If losing focus while keys are down, reset them.
+ if (gKeyboard)
+ {
+ gKeyboard->resetKeys();
+ }
+}
+
+
+BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated)
+{
+ if (gAwayTimer.getElapsedTimeF32() > MIN_AFK_TIME)
+ {
+ gAgent.clearAFK();
+ }
+
+ // HACK: We want to interpret KEY_RETURN later when it arrives as a Unicode char,
+ // not as a keydown. Otherwise when client frame rate is really low, hitting
+ // return sends your chat text before it's all entered/processed.
+ if (key == KEY_RETURN && mask == MASK_NONE)
+ {
+ return FALSE;
+ }
+
+ return gViewerKeyboard.handleKey(key, mask, repeated);
+}
+
+BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask)
+{
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level)
+{
+ return gViewerKeyboard.scanKey(key, key_down, key_up, key_level);
+}
+
+
+
+
+BOOL LLViewerWindow::handleActivate(LLWindow *window, BOOL activated)
+{
+ if (activated)
+ {
+ mActive = TRUE;
+ send_agent_resume();
+ gAgent.clearAFK();
+ if (mWindow->getFullscreen() && !mIgnoreActivate)
+ {
+ if (!gQuit)
+ {
+ if (gStartupState >= STATE_STARTED)
+ {
+ // if we're in world, show a progress bar to hide reloading of textures
+ llinfos << "Restoring GL during activate" << llendl;
+ restoreGL("Restoring...");
+ }
+ else
+ {
+ // otherwise restore immediately
+ restoreGL();
+ }
+ }
+ else
+ {
+ llwarns << "Activating while quitting" << llendl;
+ }
+ }
+
+ // Unmute audio
+ if (!gSavedSettings.getBOOL("MuteAudio"))
+ {
+ if (gAudiop) gAudiop->setMuted(FALSE);
+ F32 volume = gSavedSettings.getF32("MediaAudioVolume");
+ if(LLMediaEngine::getInstance())
+ {
+ LLMediaEngine::getInstance()->setVolume(volume);
+ LLMediaEngine::updateClass(volume);
+ }
+ }
+ }
+ else
+ {
+ mActive = FALSE;
+ gAgent.setAFK();
+ send_agent_pause();
+ if (mWindow->getFullscreen() && !mIgnoreActivate)
+ {
+ llinfos << "Stopping GL during deactivation" << llendl;
+ stopGL();
+ }
+ // Mute audio
+ if (gSavedSettings.getBOOL("MuteWhenMinimized"))
+ {
+ llinfos << "Muting audio on minimize" << llendl;
+ if (gAudiop) gAudiop->setMuted(TRUE);
+ F32 volume = 0.f;
+ LLMediaEngine::getInstance()->setVolume(volume);
+ LLMediaEngine::updateClass(volume);
+ }
+ }
+ return TRUE;
+}
+
+
+void LLViewerWindow::handleMenuSelect(LLWindow *window, S32 menu_item)
+{
+}
+
+
+BOOL LLViewerWindow::handlePaint(LLWindow *window, S32 x, S32 y, S32 width, S32 height)
+{
+#if LL_WINDOWS
+ if (gNoRender)
+ {
+ HWND window_handle = llwindow_get_hwnd(window);
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ RECT wnd_rect;
+ wnd_rect.left = 0;
+ wnd_rect.top = 0;
+ wnd_rect.bottom = 200;
+ wnd_rect.right = 500;
+
+ hdc = BeginPaint(window_handle, &ps);
+ //SetBKColor(hdc, RGB(255, 255, 255));
+ FillRect(hdc, &wnd_rect, CreateSolidBrush(RGB(255, 255, 255)));
+
+ LLString name_str;
+ gAgent.getName(name_str);
+
+ S32 len;
+ char temp_str[255];
+ sprintf(temp_str, "%s FPS %3.1f Phy FPS %2.1f Time Dil %1.3f",
+ name_str.c_str(),
+ gViewerStats->mFPSStat.getMeanPerSec(),
+ gViewerStats->mSimPhysicsFPS.getPrev(0),
+ gViewerStats->mSimTimeDilation.getPrev(0));
+ len = strlen(temp_str);
+ TextOutA(hdc, 0, 0, temp_str, len);
+
+
+ LLVector3d pos_global = gAgent.getPositionGlobal();
+ sprintf(temp_str, "Avatar pos %6.1lf %6.1lf %6.1lf", pos_global.mdV[0], pos_global.mdV[1], pos_global.mdV[2]);
+ len = strlen(temp_str);
+ TextOutA(hdc, 0, 25, temp_str, len);
+
+ TextOutA(hdc, 0, 50, "Set \"DisableRendering FALSE\" in settings.ini file to reenable", 61);
+ EndPaint(window_handle, &ps);
+ return TRUE;
+ }
+#endif
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScrollWheel(LLWindow *window, S32 clicks)
+{
+ gViewerWindow->handleScrollWheel( clicks );
+}
+
+void LLViewerWindow::handleWindowBlock(LLWindow *window)
+{
+ send_agent_pause();
+}
+
+void LLViewerWindow::handleWindowUnblock(LLWindow *window)
+{
+ send_agent_resume();
+}
+
+void LLViewerWindow::handleDataCopy(LLWindow *window, S32 data_type, void *data)
+{
+ switch (data_type)
+ {
+ case 0:
+ // received URL
+ if (LLURLSimString::unpack_data(data))
+ {
+ 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();
+ }
+ break;
+ }
+}
+
+
+//
+// Classes
+//
+LLViewerWindow::LLViewerWindow(
+ char* title, char* name,
+ S32 x, S32 y,
+ S32 width, S32 height,
+ BOOL fullscreen, BOOL ignore_pixel_depth)
+ :
+ mActive(TRUE),
+ mWantFullscreen(fullscreen),
+ mShowFullscreenProgress(FALSE),
+ mWindowRect(0, height, width, 0),
+ mVirtualWindowRect(0, height, width, 0),
+ mLeftMouseDown(FALSE),
+ mRightMouseDown(FALSE),
+ mToolTip(NULL),
+ mToolTipBlocked(FALSE),
+ mMouseInWindow( FALSE ),
+ mLastMask( MASK_NONE ),
+ mToolStored( NULL ),
+ mSuppressToolbox( FALSE ),
+ mHideCursorPermanent( FALSE ),
+ mPickPending(FALSE),
+ mIgnoreActivate( FALSE ),
+ mRenderFullFrame(FALSE)
+{
+ // Default to application directory.
+ strcpy(LLViewerWindow::sSnapshotBaseName, "Snapshot");
+ strcpy(LLViewerWindow::sMovieBaseName, "SLmovie");
+ LLViewerWindow::sSnapshotDir[0] = '\0';
+
+ mFastFrameTimer.stop();
+
+ // create window
+ mWindow = LLWindowManager::createWindow(
+ title, name, x, y, width, height, 0,
+ fullscreen,
+ gNoRender,
+ gSavedSettings.getBOOL("DisableVerticalSync"),
+ !gNoRender,
+ ignore_pixel_depth);
+#if LL_WINDOWS
+ if (!LLWinDebug::setupExceptionHandler())
+ {
+ llwarns << " Someone took over my exception handler (post createWindow)!" << llendl;
+ }
+#endif
+
+ if (NULL == mWindow)
+ {
+ LLSplashScreen::update("Shutting down...");
+#if LL_LINUX
+ llwarns << "Unable to create window, be sure screen is set at 32-bit color and your graphics driver is configured correctly. See README-linux.txt for further information."
+ << llendl;
+#else
+ llwarns << "Unable to create window, be sure screen is set at 32-bit color in Control Panels->Display->Settings"
+ << llendl;
+#endif
+ app_force_exit(1);
+ }
+
+ // Get the real window rect the window was created with (since there are various OS-dependent reasons why
+ // the size of a window or fullscreen context may have been adjusted slightly...)
+ F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
+
+ mDisplayScale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
+ mDisplayScale *= ui_scale_factor;
+ LLUI::setScaleFactor(mDisplayScale);
+
+ {
+ LLCoordWindow size;
+ mWindow->getSize(&size);
+ mWindowRect.set(0, size.mY, size.mX, 0);
+ mVirtualWindowRect.set(0, llround((F32)size.mY / mDisplayScale.mV[VY]), llround((F32)size.mX / mDisplayScale.mV[VX]), 0);
+ }
+
+ LLFontManager::initClass();
+
+ //
+ // We want to set this stuff up BEFORE we initialize the pipeline, so we can turn off
+ // stuff like AGP if we think that it'll crash the viewer.
+ //
+ gFeatureManagerp->initGraphicsFeatureMasks();
+ if (gFeatureManagerp->isSafe()
+ || (gSavedSettings.getS32("LastFeatureVersion") != gFeatureManagerp->getVersion()))
+ {
+ gFeatureManagerp->applyRecommendedFeatures();
+ }
+
+ S32 idx = gSavedSettings.getS32("GraphicsCardMemorySetting");
+ // -1 indicates use default (max)
+ if (idx == -1)
+ {
+ idx = LLViewerImageList::getMaxVideoRamSetting(-2); // get max recommended setting
+ gSavedSettings.setS32("GraphicsCardMemorySetting", idx);
+ }
+
+ if (!gNoRender)
+ {
+ //
+ // Initialize GL stuff
+ //
+
+ gPipeline.init();
+ stop_glerror();
+ initGLDefaults();
+ LLViewerImage::initClass();
+ }
+
+ //
+ // Done initing GL stuff.
+ //
+
+ // set callbacks
+ mWindow->setCallbacks(this);
+
+ // Init the image list. Must happen after GL is initialized and before the images that
+ // LLViewerWindow needs are requested.
+ gImageList.init();
+ gBumpImageList.init();
+
+ // Create container for all sub-views
+ mRootView = new LLRootView("root", mVirtualWindowRect, FALSE);
+ mRootView->setRenderInFastFrame(FALSE);
+
+ if (!gNoRender)
+ {
+ // Init default fonts
+ initFonts();
+ }
+
+ // Init Resource Manager
+ gResMgr = new LLResMgr();
+
+ // Make avatar head look forward at start
+ mCurrentMousePoint.mX = getWindowWidth() / 2;
+ mCurrentMousePoint.mY = getWindowHeight() / 2;
+
+ mPickBuffer = new U8[PICK_DIAMETER * PICK_DIAMETER * 4];
+
+ gShowOverlayTitle = gSavedSettings.getBOOL("ShowOverlayTitle");
+ mOverlayTitle = gSavedSettings.getString("OverlayTitle");
+ // Can't have spaces in settings.ini strings, so use underscores instead and convert them.
+ LLString::replaceChar(mOverlayTitle, '_', ' ');
+
+ gAwayTimer.stop();
+
+ LLAlertDialog::setDisplayCallback(alertCallback); // call this before calling any modal dialogs
+
+ // sync the keyboard's setting with the saved setting
+ gSavedSettings.getControl("NumpadControl")->firePropertyChanged();
+}
+
+void LLViewerWindow::initGLDefaults()
+{
+ //LLGLState::reset();
+ //gGLSDefault.set();
+ //LLGLState::verify(TRUE);
+
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
+
+ F32 ambient[4] = {0.f,0.f,0.f,0.f };
+ F32 diffuse[4] = {1.f,1.f,1.f,1.f };
+ glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient);
+ glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse);
+
+ glPixelStorei(GL_PACK_ALIGNMENT,1);
+ glPixelStorei(GL_UNPACK_ALIGNMENT,1);
+
+ // lights for objects
+ glShadeModel( GL_SMOOTH );
+
+ glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambient);
+
+ glCullFace(GL_BACK);
+
+ // RN: Need this for translation and stretch manip.
+ gCone.prerender();
+ gBox.prerender();
+ gSphere.prerender();
+ gCylinder.prerender();
+
+ LLVOAvatar::initVertexPrograms();
+}
+
+void LLViewerWindow::initBase()
+{
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ LLRect full_window(0, height, width, 0);
+
+ adjustRectanglesForFirstUse(full_window);
+
+ ////////////////////
+ //
+ // Set the gamma
+ //
+
+ F32 gamma = gSavedSettings.getF32("RenderGamma");
+ if (gamma != 0.0f)
+ {
+ gViewerWindow->getWindow()->setGamma(gamma);
+ }
+
+ // Create global views
+
+ // Create the floater view at the start so that other views can add children to it.
+ // (But wait to add it as a child of the root view so that it will be in front of the
+ // other views.)
+
+ // Constrain floaters to inside the menu and status bar regions.
+ LLRect floater_view_rect = full_window;
+ // make space for menu bar if we have one
+ floater_view_rect.mTop -= MENU_BAR_HEIGHT;
+ floater_view_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 2;
+
+ // Check for non-first startup
+ S32 floater_view_bottom = gSavedSettings.getS32("FloaterViewBottom");
+ if (floater_view_bottom >= 0)
+ {
+ floater_view_rect.mBottom = floater_view_bottom;
+ }
+ gFloaterView = new LLFloaterView("Floater View", floater_view_rect );
+ gFloaterView->setVisible(TRUE);
+
+ gSnapshotFloaterView = new LLSnapshotFloaterView("Snapshot Floater View", full_window);
+ gSnapshotFloaterView->setVisible(TRUE);
+
+ // Console
+ llassert( !gConsole );
+ LLRect console_rect = full_window;
+ console_rect.mTop -= 24;
+ console_rect.mBottom += STATUS_BAR_HEIGHT + 12 + 16 + 12;
+ console_rect.mLeft += 24; //gSavedSettings.getS32("StatusBarButtonWidth") + gSavedSettings.getS32("StatusBarPad");
+
+ if (gSavedSettings.getBOOL("ChatFullWidth"))
+ {
+ console_rect.mRight -= 10;
+ }
+ else
+ {
+ // Make console rect somewhat narrow so having inventory open is
+ // less of a problem.
+ console_rect.mRight = console_rect.mLeft + 2 * width / 3;
+ }
+
+ gConsole = new LLConsole(
+ "console",
+ gSavedSettings.getS32("ConsoleBufferSize"),
+ console_rect,
+ gSavedSettings.getS32("ChatFontSize"),
+ gSavedSettings.getF32("ChatPersistTime") );
+ gConsole->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
+ mRootView->addChild(gConsole);
+
+ // Debug view over the console
+ gDebugView = new LLDebugView("gDebugView", full_window);
+ gDebugView->setFollowsAll();
+ gDebugView->setVisible(TRUE);
+ mRootView->addChild(gDebugView);
+
+ // HUD elements just below floaters
+ LLRect hud_rect = full_window;
+ hud_rect.mTop -= 24;
+ hud_rect.mBottom += STATUS_BAR_HEIGHT;
+ gHUDView = new LLHUDView("hud_view", hud_rect);
+ gHUDView->setFollowsAll();
+ mRootView->addChild(gHUDView);
+
+ // Add floater view at the end so it will be on top, and give it tab priority over others
+ mRootView->addChild(gFloaterView, -1);
+ mRootView->addChild(gSnapshotFloaterView);
+
+ // notify above floaters!
+ LLRect notify_rect = full_window;
+ //notify_rect.mTop -= 24;
+ notify_rect.mBottom += STATUS_BAR_HEIGHT;
+ gNotifyBoxView = new LLNotifyBoxView("notify", notify_rect, FALSE, FOLLOWS_ALL);
+ mRootView->addChild(gNotifyBoxView, -2);
+
+ // Tooltips go above floaters
+ mToolTip = new LLTextBox( "tool tip", LLRect(0, 1, 1, 0 ) );
+ mToolTip->setHPad( 4 );
+ mToolTip->setVPad( 2 );
+ mToolTip->setColor( gColors.getColor( "ToolTipTextColor" ) );
+ mToolTip->setBorderColor( gColors.getColor( "ToolTipBorderColor" ) );
+ mToolTip->setBorderVisible( FALSE );
+ mToolTip->setBackgroundColor( gColors.getColor( "ToolTipBgColor" ) );
+ mToolTip->setBackgroundVisible( TRUE );
+ mToolTip->setDropshadowVisible( FALSE );
+ mToolTip->setBorderDropshadowVisible( TRUE );
+ mToolTip->setVisible( FALSE );
+
+ // Add the progress bar view (startup view), which overrides everything
+ mProgressView = new LLProgressView("ProgressView", full_window);
+ mRootView->addChild(mProgressView);
+ setShowProgress(FALSE);
+ setProgressCancelButtonVisible(FALSE, "");
+}
+
+
+void adjust_rect_top_left(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setLeftTopAndSize(0, window.getHeight(), r.getWidth(), r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+void adjust_rect_top_right(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setLeftTopAndSize(window.getWidth() - r.getWidth(),
+ window.getHeight(),
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+void adjust_rect_bottom_center(const LLString& control, const LLRect& window)
+{
+ LLRect r = gSavedSettings.getRect(control);
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setOriginAndSize(
+ window.getWidth()/2 - r.getWidth()/2,
+ 0,
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect(control, r);
+ }
+}
+
+// Many rectangles can't be placed until we know the screen size.
+// These rectangles have their bottom-left corner as 0,0
+void LLViewerWindow::adjustRectanglesForFirstUse(const LLRect& window)
+{
+ LLRect r;
+
+ adjust_rect_bottom_center("FloaterMoveRect", window);
+
+ adjust_rect_bottom_center("FloaterCameraRect", window);
+
+ adjust_rect_top_left("FloaterCustomizeAppearanceRect", window);
+
+ adjust_rect_top_left("FloaterLandRect5", window);
+
+ adjust_rect_top_left("FloaterFindRect2", window);
+
+ adjust_rect_top_left("FloaterGestureRect", window);
+
+ adjust_rect_top_right("FloaterMapRect", window);
+
+ adjust_rect_top_left("FloaterBuildOptionsRect", window);
+
+ // bottom-right
+ r = gSavedSettings.getRect("FloaterInventoryRect");
+ if (r.mLeft == 0 && r.mBottom == 0)
+ {
+ r.setOriginAndSize(
+ window.getWidth() - r.getWidth(),
+ 0,
+ r.getWidth(),
+ r.getHeight());
+ gSavedSettings.setRect("FloaterInventoryRect", r);
+ }
+}
+
+
+void LLViewerWindow::initWorldUI()
+{
+ pre_init_menus();
+
+ S32 height = mRootView->getRect().getHeight();
+ S32 width = mRootView->getRect().getWidth();
+ LLRect full_window(0, height, width, 0);
+
+ LLRect bar_rect(-1, STATUS_BAR_HEIGHT, width+1, -1);
+ gToolBar = new LLToolBar("toolbar", bar_rect);
+
+ LLRect chat_bar_rect(-1,CHAT_BAR_HEIGHT, width+1, -1);
+ chat_bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ gChatBar = new LLChatBar("chat", chat_bar_rect);
+
+ bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ bar_rect.translate(0, CHAT_BAR_HEIGHT-1);
+ gOverlayBar = new LLOverlayBar("overlay", bar_rect);
+
+ // panel containing chatbar, toolbar, and overlay, over floaters
+ LLRect bottom_rect(-1, 2*STATUS_BAR_HEIGHT + CHAT_BAR_HEIGHT, width+1, -1);
+ gBottomPanel = new LLBottomPanel("bottom panel", bottom_rect);
+
+ // the order here is important
+ gBottomPanel->addChild(gChatBar);
+ gBottomPanel->addChild(gToolBar);
+ gBottomPanel->addChild(gOverlayBar);
+ mRootView->addChild(gBottomPanel);
+
+ // View for hover information
+ gHoverView = new LLHoverView("gHoverView", full_window);
+ gHoverView->setVisible(TRUE);
+ mRootView->addChild(gHoverView);
+
+ //
+ // Map
+ //
+ // TODO: Move instance management into class
+ gFloaterMap = new LLFloaterMap("Map");
+ gFloaterMap->setFollows(FOLLOWS_TOP|FOLLOWS_RIGHT);
+ gFloaterMap->setVisible( gSavedSettings.getBOOL("ShowMiniMap") );
+
+ // keep onscreen
+ gFloaterView->adjustToFitScreen(gFloaterMap, FALSE);
+
+ if (gSavedSettings.getBOOL("ShowCameraControls"))
+ {
+ LLFloaterCamera::show(NULL);
+ }
+
+ if (gSavedSettings.getBOOL("ShowMovementControls"))
+ {
+ LLFloaterMove::show(NULL);
+ }
+
+ // Must have one global chat floater so it can actually store
+ // the history. JC
+ gFloaterChat = new LLFloaterChat();
+ gFloaterChat->setVisible( FALSE );
+
+ if ( gSavedPerAccountSettings.getBOOL("LogShowHistory") ) gFloaterChat->loadHistory();
+
+ gIMView = new LLIMView("gIMView", LLRect() );
+ gIMView->setFollowsAll();
+
+ LLRect morph_view_rect = full_window;
+ morph_view_rect.stretch( -STATUS_BAR_HEIGHT );
+ morph_view_rect.mTop = full_window.mTop - 32;
+ gMorphView = new LLMorphView("gMorphView", morph_view_rect );
+ mRootView->addChild(gMorphView);
+ gMorphView->setVisible(FALSE);
+
+ gFloaterMute = new LLFloaterMute();
+ gFloaterMute->setVisible(FALSE);
+
+ LLWorldMapView::initClass();
+
+ LLRect world_map_rect = gSavedSettings.getRect("FloaterWorldMapRect");
+ // if 0,0,0,0 then use fullscreen
+ if (world_map_rect.mTop == 0
+ && world_map_rect.mLeft == 0
+ && world_map_rect.mRight == 0
+ && world_map_rect.mBottom == 0)
+ {
+ world_map_rect.set(0, height-TOOL_BAR_HEIGHT, width, STATUS_BAR_HEIGHT);
+ world_map_rect.stretch(-4);
+ gSavedSettings.setRect("FloaterWorldMapRect", world_map_rect);
+ }
+ gFloaterWorldMap = new LLFloaterWorldMap();
+ gFloaterWorldMap->setVisible(FALSE);
+
+ //
+ // Tools for building
+ //
+
+ // Toolbox floater
+ init_menus();
+
+ gFloaterTools = new LLFloaterTools();
+ gFloaterTools->setVisible(FALSE);
+
+ // Status bar
+ //FIXME change this back
+ S32 menu_bar_height = gMenuBarView->getRect().getHeight();
+ LLRect root_rect = gViewerWindow->getRootView()->getRect();
+ LLRect status_rect(0, root_rect.getHeight(), root_rect.getWidth(), root_rect.getHeight() - menu_bar_height);
+ gStatusBar = new LLStatusBar("status", status_rect);
+ gStatusBar->setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_TOP);
+
+ gStatusBar->reshape(root_rect.getWidth(), gStatusBar->getRect().getHeight(), TRUE);
+ gStatusBar->translate(0, root_rect.getHeight() - gStatusBar->getRect().getHeight());
+
+ gViewerWindow->getRootView()->addChild(gStatusBar);
+}
+
+
+LLViewerWindow::~LLViewerWindow()
+{
+ gSavedSettings.setS32("FloaterViewBottom", gFloaterView->getRect().mBottom);
+
+ // Cleanup global views
+ if (gMorphView)
+ {
+ gMorphView->setVisible(FALSE);
+ }
+
+ // Delete all child views.
+ delete mRootView;
+ mRootView = NULL;
+
+ // Automatically deleted as children of mRootView. Fix the globals.
+ gFloaterTools = NULL;
+ gStatusBar = NULL;
+ gFloaterCamera = NULL;
+ gIMView = NULL;
+ gHoverView = NULL;
+
+ gFloaterView = NULL;
+ gMorphView = NULL;
+
+ gFloaterChat = NULL;
+ gFloaterMute = NULL;
+
+ gFloaterMap = NULL;
+ gHUDView = NULL;
+
+ gNotifyBoxView = NULL;
+
+ delete mToolTip;
+ mToolTip = NULL;
+
+ delete gResMgr;
+ gResMgr = NULL;
+
+ //--------------------------------------------------------
+ // Shutdown GL cleanly. Order is very important here.
+ //--------------------------------------------------------
+ LLFontGL::destroyDefaultFonts();
+ LLFontManager::cleanupClass();
+ stop_glerror();
+
+ gSky.cleanup();
+ stop_glerror();
+
+ gImageList.shutdown();
+ stop_glerror();
+
+ gBumpImageList.shutdown();
+ stop_glerror();
+
+ LLWorldMapView::cleanupTextures();
+
+ LLViewerImage::cleanupClass();
+
+ delete[] mPickBuffer;
+ mPickBuffer = NULL;
+
+ llinfos << "Stopping GL during shutdown" << llendl;
+ if (!gNoRender)
+ {
+ stopGL(FALSE);
+ stop_glerror();
+ }
+
+ if (gSelectMgr)
+ {
+ llinfos << "Cleaning up select manager" << llendl;
+ gSelectMgr->cleanup();
+ }
+
+ llinfos << "Cleaning up pipeline" << llendl;
+ gPipeline.cleanup();
+ stop_glerror();
+ llinfos << "Destroying Window" << llendl;
+ destroyWindow();
+}
+
+
+void LLViewerWindow::setCursor( ECursorType c )
+{
+ mWindow->setCursor( c );
+}
+
+void LLViewerWindow::showCursor()
+{
+ mWindow->showCursor();
+}
+
+void LLViewerWindow::hideCursor()
+{
+ // Hide tooltips
+ if(mToolTip ) mToolTip->setVisible( FALSE );
+
+ // Also hide hover info
+ if (gHoverView) gHoverView->cancelHover();
+
+ // And hide the cursor
+ mWindow->hideCursor();
+}
+
+void LLViewerWindow::sendShapeToSim()
+{
+ LLMessageSystem* msg = gMessageSystem;
+ if(!msg) return;
+ msg->newMessageFast(_PREHASH_AgentHeightWidth);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->addU32Fast(_PREHASH_CircuitCode, gMessageSystem->mOurCircuitCode);
+ msg->nextBlockFast(_PREHASH_HeightWidthBlock);
+ msg->addU32Fast(_PREHASH_GenCounter, 0);
+ U16 height16 = (U16) mWindowRect.getHeight();
+ U16 width16 = (U16) mWindowRect.getWidth();
+ msg->addU16Fast(_PREHASH_Height, height16);
+ msg->addU16Fast(_PREHASH_Width, width16);
+ gAgent.sendReliableMessage();
+}
+
+// Must be called after window is created to set up agent
+// camera variables and UI variables.
+void LLViewerWindow::reshape(S32 width, S32 height)
+{
+ // Destroying the window at quit time generates spurious
+ // reshape messages. We don't care about these, and we
+ // don't want to send messages because the message system
+ // may have been destructed.
+ if (!gQuit)
+ {
+ if (gNoRender)
+ {
+ return;
+ }
+
+ glViewport(0, 0, width, height );
+
+ if (height > 0 && gCamera)
+ {
+ gCamera->setViewHeightInPixels( height );
+ if (mWindow->getFullscreen())
+ {
+ // force to 4:3 aspect for odd resolutions
+ gCamera->setAspect( getDisplayAspectRatio() );
+ }
+ else
+ {
+ gCamera->setAspect( width / (F32) height);
+ }
+ }
+
+ // update our window rectangle
+ mWindowRect.mRight = mWindowRect.mLeft + width;
+ mWindowRect.mTop = mWindowRect.mBottom + height;
+ calcDisplayScale();
+
+ BOOL display_scale_changed = mDisplayScale != LLUI::sGLScaleFactor;
+ LLUI::setScaleFactor(mDisplayScale);
+
+ // update our window rectangle
+ mVirtualWindowRect.mRight = mVirtualWindowRect.mLeft + llround((F32)width / mDisplayScale.mV[VX]);
+ mVirtualWindowRect.mTop = mVirtualWindowRect.mBottom + llround((F32)height / mDisplayScale.mV[VY]);
+
+ setupViewport();
+
+ // Inform lower views of the change
+ // round up when converting coordinates to make sure there are no gaps at edge of window
+ LLView::sForceReshape = display_scale_changed;
+ mRootView->reshape(llceil((F32)width / mDisplayScale.mV[VX]), llceil((F32)height / mDisplayScale.mV[VY]));
+ LLView::sForceReshape = FALSE;
+
+ // clear font width caches
+ if (display_scale_changed)
+ {
+ LLHUDText::reshape();
+ }
+
+ sendShapeToSim();
+
+
+ // store the mode the user wants (even if not there yet)
+ gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
+
+ // store new settings for the mode we are in, regardless
+ if (mWindow->getFullscreen())
+ {
+ gSavedSettings.setS32("FullScreenWidth", width);
+ gSavedSettings.setS32("FullScreenHeight", height);
+ }
+ else
+ {
+ // Only save size if not maximized
+ BOOL maximized = mWindow->getMaximized();
+ gSavedSettings.setBOOL("WindowMaximized", maximized);
+
+ LLCoordScreen window_size;
+ if (!maximized
+ && mWindow->getSize(&window_size))
+ {
+ gSavedSettings.setS32("WindowWidth", window_size.mX);
+ gSavedSettings.setS32("WindowHeight", window_size.mY);
+ }
+ }
+
+ gViewerStats->setStat(LLViewerStats::ST_WINDOW_WIDTH, (F64)width);
+ gViewerStats->setStat(LLViewerStats::ST_WINDOW_HEIGHT, (F64)height);
+ }
+}
+
+
+void LLViewerWindow::draw()
+{
+#if LL_DEBUG
+ LLView::sIsDrawing = TRUE;
+#endif
+ stop_glerror();
+
+ LLUI::setLineWidth(1.f);
+ //popup alerts from the UI
+ LLAlertInfo alert;
+ while (LLPanel::nextAlert(alert))
+ {
+ alertXml(alert.mLabel, alert.mArgs);
+ }
+
+ LLUI::setLineWidth(1.f);
+ // Reset any left-over transforms
+ glMatrixMode(GL_MODELVIEW);
+
+ glLoadIdentity();
+
+ //S32 screen_x, screen_y;
+
+ // HACK for timecode debugging
+ if (gSavedSettings.getBOOL("DisplayTimecode"))
+ {
+ // draw timecode block
+ char text[256];
+
+ glLoadIdentity();
+
+ microsecondsToTimecodeString(gFrameTime,text);
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+ font->renderUTF8(text, 0,
+ llround((getWindowWidth()/2)-100.f),
+ llround((getWindowHeight()-60.f)),
+ LLColor4( 1.f, 1.f, 1.f, 1.f ),
+ LLFontGL::LEFT, LLFontGL::TOP);
+ }
+
+ // Draw all nested UI views.
+ // No translation needed, this view is glued to 0,0
+
+ glPushMatrix();
+ {
+ // scale view by UI global scale factor and aspect ratio correction factor
+ glScalef(mDisplayScale.mV[VX], mDisplayScale.mV[VY], 1.f);
+
+ LLVector2 old_scale_factor = LLUI::sGLScaleFactor;
+ if (gCamera)
+ {
+ // apply camera zoom transform (for high res screenshots)
+ F32 zoom_factor = gCamera->getZoomFactor();
+ S16 sub_region = gCamera->getZoomSubRegion();
+ if (zoom_factor > 1.f)
+ {
+ //decompose subregion number to x and y values
+ int pos_y = sub_region / llceil(zoom_factor);
+ int pos_x = sub_region - (pos_y*llceil(zoom_factor));
+ // offset for this tile
+ glTranslatef((F32)gViewerWindow->getWindowWidth() * -(F32)pos_x,
+ (F32)gViewerWindow->getWindowHeight() * -(F32)pos_y,
+ 0.f);
+ glScalef(zoom_factor, zoom_factor, 1.f);
+ LLUI::sGLScaleFactor *= zoom_factor;
+ }
+ }
+
+ {
+ LLGLSTexture gls_texture;
+ show_text_gl();
+ }
+
+ if (gToolMgr)
+ {
+ // Draw tool specific overlay on world
+ gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) )->draw();
+ }
+
+ if( gAgent.cameraMouselook() )
+ {
+ drawMouselookInstructions();
+ stop_glerror();
+ }
+
+ // Draw all nested UI views.
+ // No translation needed, this view is glued to 0,0
+ mRootView->draw();
+
+ // Draw optional on-top-of-everyone view
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view && top_view->getVisible())
+ {
+ S32 screen_x, screen_y;
+ top_view->localPointToScreen(0, 0, &screen_x, &screen_y);
+
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ LLUI::translate( (F32) screen_x, (F32) screen_y, 0.f);
+ top_view->draw();
+ LLUI::popMatrix();
+ }
+
+ // Draw tooltips
+ // Adjust their rectangle so they don't go off the top or bottom
+ // of the screen.
+ if( mToolTip && mToolTip->getVisible() )
+ {
+ glMatrixMode(GL_MODELVIEW);
+ LLUI::pushMatrix();
+ {
+ S32 tip_height = mToolTip->getRect().getHeight();
+
+ S32 screen_x, screen_y;
+ mToolTip->localPointToScreen(0, -24 - tip_height,
+ &screen_x, &screen_y);
+
+ // If tooltip would draw off the bottom of the screen,
+ // show it from the cursor tip position.
+ if (screen_y < tip_height)
+ {
+ mToolTip->localPointToScreen(0, 0, &screen_x, &screen_y);
+ }
+ LLUI::translate( (F32) screen_x, (F32) screen_y, 0);
+ mToolTip->draw();
+ }
+ LLUI::popMatrix();
+ }
+
+ if( gShowOverlayTitle && !mOverlayTitle.empty() )
+ {
+ // Used for special titles such as "Second Life - Special E3 2003 Beta"
+ const S32 DIST_FROM_TOP = 20;
+ LLGLSTexture gls_texture;
+ LLFontGL::sSansSerifBig->renderUTF8(
+ mOverlayTitle, 0,
+ llround( gViewerWindow->getWindowWidth() * 0.5f),
+ gViewerWindow->getWindowHeight() - DIST_FROM_TOP,
+ LLColor4(1, 1, 1, 0.4f),
+ LLFontGL::HCENTER, LLFontGL::TOP);
+ }
+
+ LLUI::sGLScaleFactor = old_scale_factor;
+ }
+ glPopMatrix();
+
+
+#if LL_DEBUG
+ LLView::sIsDrawing = FALSE;
+#endif
+}
+
+// Takes a single keydown event, usually when UI is visible
+BOOL LLViewerWindow::handleKey(KEY key, MASK mask)
+{
+ if (gFocusMgr.getKeyboardFocus() && !(mask & (MASK_CONTROL | MASK_ALT)))
+ {
+ // We have keyboard focus, and it's not an accelerator
+
+ 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);
+ }
+ }
+
+ // HACK look for UI editing keys
+ if (LLView::sEditingUI)
+ {
+ if (LLFloaterEditUI::handleKey(key, mask))
+ {
+ return TRUE;
+ }
+ }
+
+ // Hide tooltips on keypress
+ if(mToolTip )
+ {
+ mToolTipBlocked = TRUE; // block until next time mouse is moved
+ mToolTip->setVisible( FALSE );
+ }
+
+ // Also hide hover info on keypress
+ if (gHoverView)
+ {
+ gHoverView->cancelHover();
+
+ gHoverView->setTyping(TRUE);
+ }
+
+ // Explicit hack for debug menu.
+ if ((MASK_ALT & mask) &&
+ (MASK_CONTROL & mask) &&
+ ('D' == key || 'd' == key))
+ {
+ toggle_debug_menus(NULL);
+ }
+
+ // Example "bug" for bug reporter web page
+ if ((MASK_SHIFT & mask)
+ && (MASK_ALT & mask)
+ && (MASK_CONTROL & mask)
+ && ('H' == key || 'h' == key))
+ {
+ trigger_hippo_bug(NULL);
+ }
+
+ // handle escape key
+ if (key == KEY_ESCAPE && mask == MASK_NONE)
+ {
+ if (gMenuHolder && gMenuHolder->hideMenus())
+ {
+ return TRUE;
+ }
+ //FIXME: get this to play well with mouselook and hidden cursor modes, etc.
+ //if (gFocusMgr.getMouseCapture())
+ //{
+ // gFocusMgr.setMouseCapture(NULL, NULL);
+ // return TRUE;
+ //}
+ }
+
+ // let menus handle navigation keys
+ if (gMenuBarView && gMenuBarView->handleKey(key, mask, TRUE))
+ {
+ return TRUE;
+ }
+
+ // Traverses up the hierarchy
+ LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ if( keyboard_focus )
+ {
+ // arrow keys move avatar while chatting hack
+ if (gChatBar && gChatBar->inputEditorHasFocus())
+ {
+ if (gChatBar->getCurrentChat().empty() || gSavedSettings.getBOOL("ArrowKeysMoveAvatar"))
+ {
+ switch(key)
+ {
+ case KEY_LEFT:
+ case KEY_RIGHT:
+ case KEY_UP:
+ case KEY_DOWN:
+ case KEY_PAGE_UP:
+ case KEY_PAGE_DOWN:
+ case KEY_HOME:
+ // when chatbar is empty or ArrowKeysMoveAvatar set, pass arrow keys on to avatar...
+ return FALSE;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (keyboard_focus->handleKey(key, mask, FALSE))
+ {
+ return TRUE;
+ }
+ }
+
+ if (gToolMgr)
+ {
+ if( gToolMgr->getCurrentTool(mask)->handleKey(key, mask) )
+ {
+ return TRUE;
+ }
+ }
+
+ // Try for a new-format gesture
+ if (gGestureManager.triggerGesture(key, mask))
+ {
+ return TRUE;
+ }
+
+ // See if this is a gesture trigger. If so, eat the key and
+ // don't pass it down to the menus.
+ if (gGestureList.trigger(key, mask))
+ {
+ return TRUE;
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ //FIXME: get rid of this?
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ if( top_view->handleKey( key, mask, TRUE ) )
+ {
+ return TRUE;
+ }
+ }
+
+ // give floaters first chance to handle TAB key
+ // so frontmost floater gets focus
+ if (key == KEY_TAB)
+ {
+ // if nothing has focus, go to first or last UI element as appropriate
+ if (mask & MASK_CONTROL || gFocusMgr.getKeyboardFocus() == NULL)
+ {
+ if (gMenuHolder) gMenuHolder->hideMenus();
+
+ // if CTRL-tabbing (and not just TAB with no focus), go into window cycle mode
+ gFloaterView->setCycleMode((mask & MASK_CONTROL) != 0);
+
+ // do CTRL-TAB and CTRL-SHIFT-TAB logic
+ if (mask & MASK_SHIFT)
+ {
+ mRootView->focusPrevRoot();
+ }
+ else
+ {
+ mRootView->focusNextRoot();
+ }
+ return TRUE;
+ }
+ }
+
+ // give menus a chance to handle keys
+ if (gMenuBarView && gMenuBarView->handleAcceleratorKey(key, mask))
+ {
+ return TRUE;
+ }
+
+ // don't pass keys on to world when something in ui has focus
+ return gFocusMgr.childHasKeyboardFocus(mRootView);
+}
+
+
+BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask)
+{
+ // HACK: We delay processing of return keys until they arrive as a Unicode char,
+ // so that if you're typing chat text at low frame rate, we don't send the chat
+ // until all keystrokes have been entered. JC
+ // HACK: Numeric keypad <enter> on Mac is Unicode 3
+ // HACK: Control-M on Windows is Unicode 13
+ if ((uni_char == 13 && mask != MASK_CONTROL)
+ || (uni_char == 3 && mask == MASK_NONE))
+ {
+ return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN));
+ }
+
+ // Traverses up the hierarchy
+ LLView* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ if( keyboard_focus )
+ {
+ if (keyboard_focus->handleUnicodeChar(uni_char, FALSE))
+ {
+ return TRUE;
+ }
+
+ // Topmost view gets a chance before the hierarchy
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view && top_view->handleUnicodeChar( uni_char, FALSE ) )
+ {
+ return TRUE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+void LLViewerWindow::handleScrollWheel(S32 clicks)
+{
+ gMouseIdleTimer.reset();
+
+ // Hide tooltips
+ if( mToolTip )
+ {
+ mToolTip->setVisible( FALSE );
+ }
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
+ mouse_captor->handleScrollWheel(local_x, local_y, clicks);
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel handled by captor " << mouse_captor->getName() << llendl;
+ }
+ return;
+ }
+
+ LLView* top_view = gFocusMgr.getTopView();
+ if (top_view)
+ {
+ S32 local_x;
+ S32 local_y;
+ top_view->screenPointToLocal( mCurrentMousePoint.mX, mCurrentMousePoint.mY, &local_x, &local_y );
+ if (top_view->handleScrollWheel(local_x, local_y, clicks)) return;
+ }
+
+ if (mRootView->handleScrollWheel(mCurrentMousePoint.mX, mCurrentMousePoint.mY, clicks) )
+ {
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel" << LLView::sMouseHandlerMessage << llendl;
+ LLView::sMouseHandlerMessage = "";
+ }
+ return;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Scroll Wheel not handled by view" << llendl;
+ }
+
+ if (gWorldPointer)
+ {
+ // Zoom the camera in and out behavior
+ gAgent.handleScrollWheel(clicks);
+ }
+
+ return;
+}
+
+void LLViewerWindow::moveCursorToCenter()
+{
+#if 1 // old version
+
+#if 0 // Dave's changes - this reportedly is making the drift worse on some systems?
+ S32 x = llround((F32) mVirtualWindowRect.getWidth() / 2);
+ S32 y = llround((F32) mVirtualWindowRect.getHeight() / 2);
+#else
+ S32 x = mVirtualWindowRect.getWidth() / 2;
+ S32 y = mVirtualWindowRect.getHeight() / 2;
+#endif
+
+ //on a forced move, all deltas get zeroed out to prevent jumping
+ mCurrentMousePoint.set(x,y);
+ mLastMousePoint.set(x,y);
+ mCurrentMouseDelta.set(0,0);
+
+ LLUI::setCursorPositionScreen(x, y);
+
+#else // Richard's version - fails on intel macs
+
+ S32 x = llround((F32) mWindowRect.getWidth() / 2);
+ S32 y = llround((F32) mWindowRect.getHeight() / 2);
+
+ LLCoordWindow window_point;
+ mWindow->convertCoords(LLCoordGL(x, y), &window_point);
+ mWindow->setCursorPosition(window_point);
+
+ // read back cursor position
+ mWindow->getCursorPosition(&window_point);
+ LLCoordGL new_mouse_pos;
+ mWindow->convertCoords(window_point, &new_mouse_pos);
+ new_mouse_pos.mX = llround((F32)new_mouse_pos.mX / mDisplayScale.mV[VX]);
+ new_mouse_pos.mY = llround((F32)new_mouse_pos.mY / mDisplayScale.mV[VY]);
+
+ //on a forced move, all deltas get zeroed out to prevent jumping
+ mCurrentMousePoint = new_mouse_pos;
+ mLastMousePoint = new_mouse_pos;
+ mCurrentMouseDelta.set(0,0);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////
+//
+// Hover handlers
+//
+
+// Update UI based on stored mouse position from mouse-move
+// event processing.
+BOOL LLViewerWindow::handlePerFrameHover()
+{
+ static std::string last_handle_msg;
+
+ //RN: fix for asynchronous notification of mouse leaving window not working
+ LLCoordWindow mouse_pos;
+ mWindow->getCursorPosition(&mouse_pos);
+ if (mouse_pos.mX < 0 ||
+ mouse_pos.mY < 0 ||
+ mouse_pos.mX > mWindowRect.getWidth() ||
+ mouse_pos.mY > mWindowRect.getHeight())
+ {
+ mMouseInWindow = FALSE;
+ }
+ else
+ {
+ mMouseInWindow = TRUE;
+ }
+
+ S32 dx = mCurrentMousePoint.mX - mLastMousePoint.mX;
+ S32 dy = mCurrentMousePoint.mY - mLastMousePoint.mY;
+ mCurrentMouseDelta.set(dx,dy);
+ LLVector2 mouse_vel((F32)dx, (F32)dy);
+ mMouseVelocityStat.addValue(mouse_vel.magVec());
+
+ if (gNoRender)
+ {
+ return TRUE;
+ }
+
+ S32 x = mCurrentMousePoint.mX;
+ S32 y = mCurrentMousePoint.mY;
+ MASK mask = gKeyboard->currentMask(TRUE);
+
+ // clean up current focus
+ LLUICtrl* cur_focus = gFocusMgr.getKeyboardFocus();
+ if (cur_focus)
+ {
+ if (!cur_focus->isInVisibleChain() || !cur_focus->isInEnabledChain())
+ {
+ gFocusMgr.releaseFocusIfNeeded(cur_focus);
+
+ LLView* parent = cur_focus->getParent();
+ LLView* focus_root = cur_focus->findRootMostFocusRoot();
+ while(parent)
+ {
+ if (parent->isCtrl() &&
+ (((LLUICtrl*)parent)->hasTabStop() || parent == focus_root) &&
+ !((LLUICtrl*)parent)->getIsChrome() &&
+ parent->isInVisibleChain() &&
+ parent->isInEnabledChain())
+ {
+ if (!parent->focusFirstItem())
+ {
+ ((LLUICtrl*)parent)->setFocus(TRUE);
+ }
+ break;
+ }
+ parent = parent->getParent();
+ }
+ }
+ else if (cur_focus->isFocusRoot())
+ {
+ // focus roots keep trying to delegate focus to their first valid descendant
+ // this assumes that focus roots are not valid focus holders on their own
+ cur_focus->focusFirstItem();
+ }
+ }
+
+ // Show joints while in edit mode and hold down alt key.
+ if (gHUDManager)
+ {
+ if (gSavedSettings.getBOOL("AltShowsPhysical")
+ || (gFloaterTools && gFloaterTools->getVisible()))
+ {
+ gHUDManager->toggleShowPhysical( mask & MASK_ALT );
+ }
+ }
+
+ BOOL handled = FALSE;
+
+ BOOL handled_by_top_view = FALSE;
+ LLView* top_view = gFocusMgr.getTopView();
+
+ LLMouseHandler* mouse_captor = gFocusMgr.getMouseCapture();
+ if( mouse_captor )
+ {
+ // Pass hover events to object capturing mouse events.
+ S32 local_x;
+ S32 local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = mouse_captor->handleHover(local_x, local_y, mask);
+ if (LLView::sDebugMouseHandling)
+ {
+ llinfos << "Hover handled by captor " << mouse_captor->getName() << llendl;
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover not handled by mouse captor" << llendl;
+ }
+ }
+ else
+ {
+ if (top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ handled = top_view->pointInView(local_x, local_y) && top_view->handleHover(local_x, local_y, mask);
+ handled_by_top_view = TRUE;
+ }
+
+ if ( !handled )
+ {
+ // x and y are from last time mouse was in window
+ // mMouseInWindow tracks *actual* mouse location
+ if (mMouseInWindow && mRootView->handleHover(x, y, mask) )
+ {
+ if (LLView::sDebugMouseHandling && LLView::sMouseHandlerMessage != last_handle_msg)
+ {
+ last_handle_msg = LLView::sMouseHandlerMessage;
+ llinfos << "Hover" << LLView::sMouseHandlerMessage << llendl;
+ }
+ LLView::sMouseHandlerMessage = "";
+ handled = TRUE;
+ }
+ else if (LLView::sDebugMouseHandling)
+ {
+ if (last_handle_msg != "")
+ {
+ last_handle_msg = "";
+ llinfos << "Hover not handled by view" << llendl;
+ }
+ }
+ }
+
+ if( !handled )
+ {
+ lldebugst(LLERR_USER_INPUT) << "hover not handled by top view or root" << llendl;
+ }
+ }
+
+ //FIXME: sometimes tools handle the mouse as a captor, so this
+ // logic is a little confusing
+ LLTool *tool = NULL;
+ if (gToolMgr && gHoverView)
+ {
+ tool = gToolMgr->getCurrentTool(mask);
+
+ if(!handled && tool)
+ {
+ handled = tool->handleHover(x, y, mask);
+
+ if (!mWindow->isCursorHidden())
+ {
+ gHoverView->updateHover(tool);
+ }
+ }
+ else
+ {
+ // Cancel hovering if any UI element handled the event.
+ gHoverView->cancelHover();
+ }
+
+ // Suppress the toolbox view if our source tool was the pie tool,
+ // and we've overridden to something else.
+ mSuppressToolbox =
+ (gToolMgr->getCurrentTool(MASK_NONE) == gToolPie) &&
+ (gToolMgr->getCurrentTool(mask) != gToolPie);
+
+ }
+
+ //llinfos << (mToolTipBlocked ? "BLOCKED" : "NOT BLOCKED") << llendl;
+ // Show a new tool tip (or update one that is alrady shown)
+ BOOL tool_tip_handled = FALSE;
+ LLString tool_tip_msg;
+ F32 tooltip_delay = gSavedSettings.getF32( "ToolTipDelay" );
+ //HACK: hack for tool-based tooltips which need to pop up more quickly
+ //Also for show xui names as tooltips debug mode
+ if ((mouse_captor && !mouse_captor->isView()) || LLUI::sShowXUINames)
+ {
+ tooltip_delay = gSavedSettings.getF32( "DragAndDropToolTipDelay" );
+ }
+ if( handled &&
+ !mToolTipBlocked &&
+ (gMouseIdleTimer.getElapsedTimeF32() > tooltip_delay) &&
+ !mWindow->isCursorHidden() )
+ {
+ LLRect screen_sticky_rect;
+
+ if (mouse_captor)
+ {
+ S32 local_x, local_y;
+ mouse_captor->screenPointToLocal( x, y, &local_x, &local_y );
+ tool_tip_handled = mouse_captor->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect );
+ }
+ else if (handled_by_top_view)
+ {
+ S32 local_x, local_y;
+ top_view->screenPointToLocal( x, y, &local_x, &local_y );
+ tool_tip_handled = top_view->handleToolTip( local_x, local_y, tool_tip_msg, &screen_sticky_rect );
+ }
+ else
+ {
+ tool_tip_handled = mRootView->handleToolTip(x, y, tool_tip_msg, &screen_sticky_rect );
+ }
+
+ if( tool_tip_handled && !tool_tip_msg.empty() )
+ {
+ mToolTipStickyRect = screen_sticky_rect;
+ mToolTip->setWrappedText( tool_tip_msg, 200 );
+ mToolTip->reshapeToFitText();
+ mToolTip->setOrigin( x, y );
+ LLRect virtual_window_rect(0, getWindowHeight(), getWindowWidth(), 0);
+ mToolTip->translateIntoRect( virtual_window_rect, FALSE );
+ mToolTip->setVisible( TRUE );
+ }
+ }
+
+ if (tool != gToolNull && tool != gToolDragAndDrop && !gSavedSettings.getBOOL("FreezeTime"))
+ {
+ LLMouseHandler *captor = gFocusMgr.getMouseCapture();
+ // With the null tool or drag and drop tool, don't muck
+ // with visibility.
+
+ if (gFloaterTools->isMinimized() ||
+ (tool != gToolPie // not default tool
+ && tool != gToolGun // not coming out of mouselook
+ && !mSuppressToolbox // not override in third person
+ && gCurrentToolset != gFaceEditToolset // not special mode
+ && gCurrentToolset != gMouselookToolset
+ && (!captor || captor->isView())) // not dragging
+ )
+ {
+ // Force floater tools to be visible (unless minimized)
+ if (!gFloaterTools->isMinimized() && !gFloaterTools->getVisible())
+ {
+ gFloaterTools->open();
+ }
+ // Update the location of the blue box tool popup
+ LLCoordGL select_center_screen;
+ gFloaterTools->updatePopup( select_center_screen, mask );
+ }
+ else
+ {
+ gFloaterTools->setVisible(FALSE);
+ }
+ }
+ if (gToolBar)
+ {
+ gToolBar->refresh();
+ }
+
+ if (gChatBar)
+ {
+ gChatBar->refresh();
+ }
+
+ if (gOverlayBar)
+ {
+ gOverlayBar->refresh();
+ }
+
+ // Update rectangles for the various toolbars
+ if (gToolBar && gChatBar && gOverlayBar && gNotifyBoxView && gConsole)
+ {
+ LLRect bar_rect(-1, STATUS_BAR_HEIGHT, getWindowWidth()+1, -1);
+ if (gToolBar->getVisible())
+ {
+ gToolBar->setRect(bar_rect);
+ bar_rect.translate(0, STATUS_BAR_HEIGHT-1);
+ }
+
+ if (gChatBar->getVisible())
+ {
+ // fix up the height
+ LLRect chat_bar_rect = bar_rect;
+ chat_bar_rect.mTop = chat_bar_rect.mBottom + CHAT_BAR_HEIGHT + 1;
+ gChatBar->setRect(chat_bar_rect);
+ bar_rect.translate(0, CHAT_BAR_HEIGHT-1);
+ }
+
+ LLRect notify_box_rect = gNotifyBoxView->getRect();
+ notify_box_rect.mBottom = bar_rect.mBottom;
+ gNotifyBoxView->reshape(notify_box_rect.getWidth(), notify_box_rect.getHeight());
+ gNotifyBoxView->setRect(notify_box_rect);
+
+ // make sure floaters snap to visible rect by adjusting floater view rect
+ LLRect floater_rect = gFloaterView->getRect();
+ if (floater_rect.mBottom != bar_rect.mBottom+1)
+ {
+ floater_rect.mBottom = bar_rect.mBottom+1;
+ // Don't bounce the floaters up and down.
+ gFloaterView->reshape(floater_rect.getWidth(), floater_rect.getHeight(),
+ TRUE, ADJUST_VERTICAL_NO);
+ gFloaterView->setRect(floater_rect);
+ }
+
+ if (gOverlayBar->getVisible())
+ {
+ LLRect overlay_rect = bar_rect;
+ overlay_rect.mTop = overlay_rect.mBottom + OVERLAY_BAR_HEIGHT;
+
+ // Fitt's Law: Push buttons flush with bottom of screen if
+ // nothing else visible.
+ if (!gToolBar->getVisible()
+ && !gChatBar->getVisible())
+ {
+ // FIXME: this is highly depenent on the XML describing the position of the buttons
+ overlay_rect.translate(0, 0);
+ }
+
+ gOverlayBar->setRect(overlay_rect);
+ gOverlayBar->updateRect();
+ bar_rect.translate(0, gOverlayBar->getRect().getHeight());
+
+ gFloaterView->setSnapOffsetBottom(OVERLAY_BAR_HEIGHT);
+ }
+ else
+ {
+ gFloaterView->setSnapOffsetBottom(0);
+ }
+
+ // fix rectangle of bottom panel focus indicator
+ if(gBottomPanel && gBottomPanel->getFocusIndicator())
+ {
+ LLRect focus_rect = gBottomPanel->getFocusIndicator()->getRect();
+ focus_rect.mTop = (gToolBar->getVisible() ? STATUS_BAR_HEIGHT : 0) +
+ (gChatBar->getVisible() ? CHAT_BAR_HEIGHT : 0) - 2;
+ gBottomPanel->getFocusIndicator()->setRect(focus_rect);
+ }
+
+ // Always update console
+ LLRect console_rect = gConsole->getRect();
+ console_rect.mBottom = bar_rect.mBottom + 8;
+ gConsole->reshape(console_rect.getWidth(), console_rect.getHeight());
+ gConsole->setRect(console_rect);
+ }
+
+ mLastMousePoint = mCurrentMousePoint;
+
+ // last ditch force of edit menu to selection manager
+ if (gEditMenuHandler == NULL && gSelectMgr && gSelectMgr->getObjectCount())
+ {
+ gEditMenuHandler = gSelectMgr;
+ }
+
+ if (gFloaterView->getCycleMode())
+ {
+ // sync all floaters with their focus state
+ gFloaterView->highlightFocusedFloater();
+ gSnapshotFloaterView->highlightFocusedFloater();
+ if ((gKeyboard->currentMask(TRUE) & MASK_CONTROL) == 0)
+ {
+ // control key no longer held down, finish cycle mode
+ gFloaterView->setCycleMode(FALSE);
+
+ gFloaterView->syncFloaterTabOrder();
+ }
+ else
+ {
+ // user holding down CTRL, don't update tab order of floaters
+ }
+ }
+ else
+ {
+ // update focused floater
+ gFloaterView->highlightFocusedFloater();
+ gSnapshotFloaterView->highlightFocusedFloater();
+ // make sure floater visible order is in sync with tab order
+ gFloaterView->syncFloaterTabOrder();
+ }
+
+ if (gSavedSettings.getBOOL("ChatBarStealsFocus") && gChatBar && gFocusMgr.getKeyboardFocus() == NULL && gChatBar->getVisible())
+ {
+ gChatBar->startChat(NULL);
+ }
+
+ // sync land selection with edit and about land dialogs
+ if (gParcelMgr && !LLFloaterLand::floaterVisible() && (!gFloaterTools || !gFloaterTools->getVisible()))
+ {
+ gParcelMgr->deselectLand();
+ }
+
+ return handled;
+}
+
+
+void LLViewerWindow::saveLastMouse(const LLCoordGL &point)
+{
+ // Store last mouse location.
+ // If mouse leaves window, pretend last point was on edge of window
+ if (point.mX < 0)
+ {
+ mCurrentMousePoint.mX = 0;
+ }
+ else if (point.mX > getWindowWidth())
+ {
+ mCurrentMousePoint.mX = getWindowWidth();
+ }
+ else
+ {
+ mCurrentMousePoint.mX = point.mX;
+ }
+
+ if (point.mY < 0)
+ {
+ mCurrentMousePoint.mY = 0;
+ }
+ else if (point.mY > getWindowHeight() )
+ {
+ mCurrentMousePoint.mY = getWindowHeight();
+ }
+ else
+ {
+ mCurrentMousePoint.mY = point.mY;
+ }
+}
+
+
+// Draws the selection outlines for the currently selected objects
+// Must be called after displayObjects is called, which sets the mGLName parameter
+// NOTE: This function gets called 3 times:
+// render_ui_3d: FALSE, FALSE, TRUE
+// renderObjectsForSelect: TRUE, pick_parcel_wall, FALSE
+// render_hud_elements: FALSE, FALSE, FALSE
+void LLViewerWindow::renderSelections( BOOL for_gl_pick, BOOL pick_parcel_walls, BOOL for_hud )
+{
+ LLViewerObject* object;
+
+ if (!for_hud && !for_gl_pick)
+ {
+ // Call this once and only once
+ gSelectMgr->updateSilhouettes();
+ }
+
+ // Draw fence around land selections
+ if (for_gl_pick)
+ {
+ if (pick_parcel_walls)
+ {
+ gParcelMgr->renderParcelCollision();
+ }
+ }
+ else if (( for_hud && gSelectMgr->getSelectType() == SELECT_TYPE_HUD) ||
+ (!for_hud && gSelectMgr->getSelectType() != SELECT_TYPE_HUD))
+ {
+ gSelectMgr->renderSilhouettes(for_hud);
+
+ stop_glerror();
+
+ // setup HUD render
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount())
+ {
+ LLBBox hud_bbox = gAgent.getAvatarObject()->getHUDBBox();
+
+ // set up transform to encompass bounding box of HUD
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ F32 depth = llmax(1.f, hud_bbox.getExtentLocal().mV[VX] * 1.1f);
+ glOrtho(-0.5f * gCamera->getAspect(), 0.5f * gCamera->getAspect(), -0.5f, 0.5f, 0.f, depth);
+
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+ glLoadMatrixf(OGL_TO_CFR_ROTATION); // Load Cory's favorite reference frame
+ glTranslatef(-hud_bbox.getCenterLocal().mV[VX] + (depth *0.5f), 0.f, 0.f);
+ }
+
+ // Render light for editing
+ if (LLSelectMgr::sRenderLightRadius)
+ {
+ LLGLEnable gls_blend(GL_BLEND);
+ LLGLEnable gls_cull(GL_CULL_FACE);
+ LLGLDepthTest gls_depth(GL_TRUE, GL_TRUE);
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD)
+ {
+ F32 zoom = gAgent.getAvatarObject()->mHUDCurZoom;
+ glScalef(zoom, zoom, zoom);
+ }
+ for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ LLDrawable* drawable = object->mDrawable;
+ if (drawable && drawable->isLight())
+ {
+ LLVOVolume* vovolume = drawable->getVOVolume();
+ glPushMatrix();
+
+ LLVector3 center = drawable->getPositionAgent();
+ glTranslatef(center[0], center[1], center[2]);
+ F32 scale = vovolume->getLightRadius();
+ glScalef(scale, scale, scale);
+
+ LLColor4 color(vovolume->getLightColor(), .5f);
+ glColor4fv(color.mV);
+
+ F32 pixel_area = 100000.f;
+ // Render Outside
+ gSphere.render(pixel_area);
+
+ // Render Inside
+ glCullFace(GL_FRONT);
+ gSphere.render(pixel_area);
+ glCullFace(GL_BACK);
+
+ glPopMatrix();
+ }
+ }
+ glPopMatrix();
+ }
+
+ // NOTE: The average position for the axis arrows of the selected objects should
+ // not be recalculated at this time. If they are, then group rotations will break.
+
+ // Draw arrows at average center of all selected objects
+ LLTool* tool = gToolMgr->getCurrentTool( gKeyboard->currentMask(TRUE) );
+ if (tool)
+ {
+ if(tool->isAlwaysRendered())
+ {
+ tool->render();
+ }
+ else
+ {
+ if( !gSelectMgr->isEmpty() )
+ {
+ BOOL moveable_object_selected = FALSE;
+ BOOL all_selected_objects_move = TRUE;
+ BOOL all_selected_objects_modify = TRUE;
+ BOOL selecting_linked_set = gSavedSettings.getBOOL("SelectLinkedSet");
+ for( object = gSelectMgr->getFirstObject(); object; object = gSelectMgr->getNextObject() )
+ {
+ BOOL this_object_movable = FALSE;
+ if (object->permMove() && (object->permModify() || selecting_linked_set))
+ {
+ moveable_object_selected = TRUE;
+ this_object_movable = TRUE;
+ }
+ all_selected_objects_move = all_selected_objects_move && this_object_movable;
+ all_selected_objects_modify = all_selected_objects_modify && object->permModify();
+ }
+
+ BOOL draw_handles = TRUE;
+
+ if (tool == gToolTranslate && (!moveable_object_selected || !all_selected_objects_move))
+ {
+ draw_handles = FALSE;
+ }
+
+ if (tool == gToolRotate && (!moveable_object_selected || !all_selected_objects_move))
+ {
+ draw_handles = FALSE;
+ }
+
+ if ( !all_selected_objects_modify && tool == gToolStretch )
+ {
+ draw_handles = FALSE;
+ }
+
+ if( draw_handles )
+ {
+ tool->render();
+ }
+ }
+ }
+ if (gSelectMgr->getSelectType() == SELECT_TYPE_HUD && gSelectMgr->getObjectCount())
+ {
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ stop_glerror();
+ }
+ }
+ }
+}
+
+// Return a point near the clicked object representative of the place the object was clicked.
+LLVector3d LLViewerWindow::clickPointInWorldGlobal(S32 x, S32 y_from_bot, LLViewerObject* clicked_object) const
+{
+ // create a normalized vector pointing from the camera center into the
+ // world at the location of the mouse click
+ LLVector3 mouse_direction_global = mouseDirectionGlobal( x, y_from_bot );
+
+ LLVector3d relative_object = clicked_object->getPositionGlobal() - gAgent.getCameraPositionGlobal();
+
+ // make mouse vector as long as object vector, so it touchs a point near
+ // where the user clicked on the object
+ mouse_direction_global *= (F32) relative_object.magVec();
+
+ LLVector3d new_pos;
+ new_pos.setVec(mouse_direction_global);
+ // transform mouse vector back to world coords
+ new_pos += gAgent.getCameraPositionGlobal();
+
+ return new_pos;
+}
+
+
+BOOL LLViewerWindow::clickPointOnSurfaceGlobal(const S32 x, const S32 y, LLViewerObject *objectp, LLVector3d &point_global) const
+{
+ BOOL intersect = FALSE;
+
+// U8 shape = objectp->mPrimitiveCode & LL_PCODE_BASE_MASK;
+ if (!intersect)
+ {
+ point_global = clickPointInWorldGlobal(x, y, objectp);
+ llinfos << "approx intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
+ }
+ else
+ {
+ llinfos << "good intersection at " << (objectp->getPositionGlobal() - point_global) << llendl;
+ }
+
+ return intersect;
+}
+
+void LLViewerWindow::hitObjectOrLandGlobalAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent, BOOL pick_parcel_walls)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
+ S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
+
+ F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32();
+
+ BOOL in_build_mode = gFloaterTools && gFloaterTools->getVisible();
+ if (in_build_mode || LLDrawPoolAlpha::sShowDebugAlpha)
+ {
+ // build mode allows interaction with all transparent objects
+ // "Show Debug Alpha" means no object actually transparent
+ pick_transparent = TRUE;
+ }
+ gPickTransparent = pick_transparent;
+
+ if (gPickTransparent)
+ {
+ gPickAlphaTargetThreshold = 0.f;
+ gPickAlphaThreshold = llmax(gPickAlphaTargetThreshold, gPickAlphaThreshold - (delta_time * 0.7f));
+ }
+ else
+ {
+ gPickAlphaTargetThreshold = 1.f;
+ gPickAlphaThreshold = llmin(gPickAlphaTargetThreshold, gPickAlphaThreshold + (delta_time * 0.7f));
+ }
+
+ gUseGLPick = FALSE;
+ mPickCallback = callback;
+
+ // Default to not hitting anything
+ gLastHitPosGlobal.zeroVec();
+ gLastHitObjectOffset.zeroVec();
+ gLastHitObjectID.setNull();
+ gLastHitObjectFace = -1;
+
+ gLastHitNonFloraPosGlobal.zeroVec();
+ gLastHitNonFloraObjectOffset.zeroVec();
+ gLastHitNonFloraObjectID.setNull();
+ gLastHitNonFloraObjectFace = -1;
+
+ gLastHitParcelWall = FALSE;
+
+ LLCamera pick_camera;
+ pick_camera.setOrigin(gCamera->getOrigin());
+ pick_camera.setOriginAndLookAt(gCamera->getOrigin(),
+ gCamera->getUpAxis(),
+ gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
+ pick_camera.setView(0.5f*DEG_TO_RAD);
+ pick_camera.setNear(gCamera->getNear());
+ pick_camera.setFar(gCamera->getFar());
+ pick_camera.setAspect(1.f);
+
+ // save our drawing state
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // build perspective transform and picking viewport
+ // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
+ // Don't limit the select distance for this pick.
+ // make viewport big enough to handle antialiased frame buffers
+ gCamera->setPerspective(FOR_SELECTION, scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4, FALSE);
+ pick_camera.calcAgentFrustumPlanes(gCamera->mAgentFrustum);
+ // make viewport big enough to handle antialiased frame buffers
+ glViewport(scaled_x - (PICK_HALF_WIDTH + 2), scaled_y - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4);
+ stop_glerror();
+
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ // Draw the objects so the user can select them.
+ // The starting ID is 1, since land is zero.
+ gObjectList.renderObjectsForSelect(pick_camera, pick_parcel_walls);
+
+ stop_glerror();
+
+ // restore drawing state
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ setupViewport();
+
+ mPickPoint.set(x, y_from_bot);
+ mPickOffset.set(0, 0);
+ mPickMask = mask;
+ mPickPending = TRUE;
+
+ // delay further event processing until we receive results of pick
+ mWindow->delayInputProcessing();
+}
+
+void LLViewerWindow::hitUIElementImmediate(S32 x, S32 y, void (*callback)(S32 x, S32 y, MASK mask))
+{
+ // Performs the GL UI pick.
+ // Stores its results in global, gLastHitUIElement
+ if (gNoRender)
+ {
+ return;
+ }
+
+ hitUIElementAsync(x, y, gKeyboard->currentMask(TRUE), NULL);
+ performPick();
+ if (callback)
+ {
+ callback(x, y, gKeyboard->currentMask(TRUE));
+ }
+}
+
+//RN: this currently doesn't do anything
+void LLViewerWindow::hitUIElementAsync(S32 x, S32 y_from_bot, MASK mask, void (*callback)(S32 x, S32 y, MASK mask))
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+// F32 delta_time = gAlphaFadeTimer.getElapsedTimeAndResetF32();
+
+ gUseGLPick = FALSE;
+ mPickCallback = callback;
+
+ // Default to not hitting anything
+ gLastHitUIElement = 0;
+
+ LLCamera pick_camera;
+ pick_camera.setOrigin(gCamera->getOrigin());
+ pick_camera.setOriginAndLookAt(gCamera->getOrigin(),
+ gCamera->getUpAxis(),
+ gCamera->getOrigin() + mouseDirectionGlobal(x, y_from_bot));
+ pick_camera.setView(0.5f*DEG_TO_RAD);
+ pick_camera.setNear(gCamera->getNear());
+ pick_camera.setFar(gCamera->getFar());
+ pick_camera.setAspect(1.f);
+
+ // save our drawing state
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+
+ // build orthogonal transform and picking viewport
+ // Perform pick on a PICK_DIAMETER x PICK_DIAMETER pixel region around cursor point.
+ // Don't limit the select distance for this pick.
+ gViewerWindow->setup2DRender();
+ const LLVector2& display_scale = gViewerWindow->getDisplayScale();
+ glScalef(display_scale.mV[VX], display_scale.mV[VY], 1.f);
+
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+
+ // make viewport big enough to handle antialiased frame buffers
+ glViewport(x - (PICK_HALF_WIDTH + 2), y_from_bot - (PICK_HALF_WIDTH + 2), PICK_DIAMETER + 4, PICK_DIAMETER + 4);
+ stop_glerror();
+
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
+
+ // Draw the objects so the user can select them.
+ // The starting ID is 1, since land is zero.
+ //gViewerWindow->drawForSelect();
+
+ stop_glerror();
+
+ // restore drawing state
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ setupViewport();
+
+ mPickPoint.set(x, y_from_bot);
+ mPickOffset.set(0, 0);
+ mPickMask = mask;
+ mPickPending = TRUE;
+}
+
+void LLViewerWindow::performPick()
+{
+ if (gNoRender || !mPickPending)
+ {
+ return;
+ }
+
+ finishFastFrame();
+
+ mPickPending = FALSE;
+ U32 te_offset = NO_FACE;
+
+ // find pick region that is fully onscreen
+ LLCoordGL scaled_pick_point = mPickPoint;
+ scaled_pick_point.mX = llclamp(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayWidth() - PICK_HALF_WIDTH);
+ scaled_pick_point.mY = llclamp(llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]), PICK_HALF_WIDTH, gViewerWindow->getWindowDisplayHeight() - PICK_HALF_WIDTH);
+
+ glReadPixels(scaled_pick_point.mX - PICK_HALF_WIDTH, scaled_pick_point.mY - PICK_HALF_WIDTH, PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+
+ S32 pixel_index = PICK_HALF_WIDTH * PICK_DIAMETER + PICK_HALF_WIDTH;
+ S32 name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+
+ if (name >= (S32)GL_NAME_UI_RESERVED && name < (S32)GL_NAME_INDEX_OFFSET)
+ {
+ // hit a UI element
+ gLastHitUIElement = name;
+ if (mPickCallback)
+ {
+ mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
+ }
+ }
+
+ //imdebug("rgba rbga=bbba b=8 w=%d h=%d %p", PICK_DIAMETER, PICK_DIAMETER, mPickBuffer);
+
+ S32 x_offset = mPickPoint.mX - llround((F32)scaled_pick_point.mX / mDisplayScale.mV[VX]);
+ S32 y_offset = mPickPoint.mY - llround((F32)scaled_pick_point.mY / mDisplayScale.mV[VY]);
+
+
+ // we hit nothing, scan surrounding pixels for something useful
+ if (!name)
+ {
+ S32 closest_distance = 10000;
+ //S32 closest_pick_name = 0;
+ for (S32 col = 0; col < PICK_DIAMETER; col++)
+ {
+ for (S32 row = 0; row < PICK_DIAMETER; row++)
+ {
+ S32 distance_squared = (llabs(col - x_offset - PICK_HALF_WIDTH) * llabs(col - x_offset - PICK_HALF_WIDTH)) + (llabs(row - y_offset - PICK_HALF_WIDTH) * llabs(row - y_offset - PICK_HALF_WIDTH));
+ pixel_index = row * PICK_DIAMETER + col;
+ S32 test_name = (U32)mPickBuffer[(pixel_index * 4) + 0] << 16 | (U32)mPickBuffer[(pixel_index * 4) + 1] << 8 | (U32)mPickBuffer[(pixel_index * 4) + 2];
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+ if (test_name && distance_squared < closest_distance)
+ {
+ closest_distance = distance_squared;
+ name = test_name;
+ gLastPickAlpha = mPickBuffer[(pixel_index * 4) + 3];
+ mPickOffset.mX = col - PICK_HALF_WIDTH;
+ mPickOffset.mY = row - PICK_HALF_WIDTH;
+ }
+ }
+ }
+ }
+
+ if (name)
+ {
+ mPickPoint.mX += llround((F32)mPickOffset.mX * mDisplayScale.mV[VX]);
+ mPickPoint.mY += llround((F32)mPickOffset.mY * mDisplayScale.mV[VY]);
+ }
+
+ if (gPickFaces)
+ {
+ te_offset = ((U32)name >> 20);
+ name &= 0x000fffff;
+ // don't clear gPickFaces, as we still need to check for UV coordinates
+ }
+
+ LLViewerObject *objectp = NULL;
+
+ // Frontmost non-foreground object that isn't trees or grass
+ LLViewerObject* nonflora_objectp = NULL;
+ S32 nonflora_name = -1;
+ S32 nonflora_te_offset = NO_FACE;
+
+ if (name == (S32)GL_NAME_PARCEL_WALL)
+ {
+ gLastHitParcelWall = TRUE;
+ }
+
+ gLastHitHUDIcon = NULL;
+
+ objectp = gObjectList.getSelectedObject(name);
+ if (objectp)
+ {
+ if (objectp->mbCanSelect)
+ {
+ te_offset = (te_offset == 16) ? NO_FACE : te_offset;
+
+ // If the hit object isn't a plant, store it as the frontmost non-flora object.
+ LLPCode pcode = objectp->getPCode();
+ if( (LL_PCODE_LEGACY_GRASS != pcode) &&
+ (LL_PCODE_LEGACY_TREE != pcode) &&
+ (LL_PCODE_TREE_NEW != pcode))
+ {
+ nonflora_objectp = objectp;
+ nonflora_name = name;
+ nonflora_te_offset = te_offset;
+ }
+ }
+ else
+ {
+ //llinfos << "Hit object you can't select" << llendl;
+ }
+ }
+ else
+ {
+ // was this name referring to a hud icon?
+ gLastHitHUDIcon = LLHUDIcon::handlePick(name);
+ }
+
+ analyzeHit(
+ mPickPoint.mX, mPickPoint.mY, objectp, te_offset,
+ &gLastHitObjectID, &gLastHitObjectFace, &gLastHitPosGlobal, &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord );
+
+ if (objectp && !gLastHitObjectID.isNull())
+ {
+ gLastHitObjectOffset = gAgent.calcFocusOffset(objectp, mPickPoint.mX, mPickPoint.mY);
+ }
+
+ if( objectp == nonflora_objectp )
+ {
+ gLastHitNonFloraObjectID = gLastHitObjectID;
+ gLastHitNonFloraObjectFace = gLastHitObjectFace;
+ gLastHitNonFloraPosGlobal = gLastHitPosGlobal;
+ gLastHitNonFloraObjectOffset= gLastHitObjectOffset;
+ }
+ else
+ {
+ analyzeHit( mPickPoint.mX, mPickPoint.mY, nonflora_objectp, nonflora_te_offset,
+ &gLastHitNonFloraObjectID, &gLastHitNonFloraObjectFace, &gLastHitNonFloraPosGlobal,
+ &gLastHitLand, &gLastHitUCoord, &gLastHitVCoord);
+
+ if( nonflora_objectp )
+ {
+ gLastHitNonFloraObjectOffset = gAgent.calcFocusOffset(nonflora_objectp, mPickPoint.mX, mPickPoint.mY);
+ }
+ }
+
+ if (mPickCallback)
+ {
+ mPickCallback(mPickPoint.mX, mPickPoint.mY, mPickMask);
+ }
+
+ gPickFaces = FALSE;
+}
+
+// Performs the GL object/land pick.
+// Stores its results in globals, gHit*
+void LLViewerWindow::hitObjectOrLandGlobalImmediate(S32 x, S32 y_from_bot, void (*callback)(S32 x, S32 y, MASK mask), BOOL pick_transparent)
+{
+ if (gNoRender)
+ {
+ return;
+ }
+
+ hitObjectOrLandGlobalAsync(x, y_from_bot, gKeyboard->currentMask(TRUE), NULL, pick_transparent);
+ performPick();
+ if (callback)
+ {
+ callback(x, y_from_bot, gKeyboard->currentMask(TRUE));
+ }
+}
+
+LLViewerObject* LLViewerWindow::getObjectUnderCursor(const F32 depth)
+{
+ S32 x = getCurrentMouseX();
+ S32 y = getCurrentMouseY();
+
+ LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
+ LLVector3 camera_pos_global = gCamera->getOrigin();
+ LLVector3 pick_end = camera_pos_global + mouse_direction_global * depth;
+ LLVector3 collision_point;
+ return gPipeline.pickObject(camera_pos_global, pick_end, collision_point);
+}
+
+void LLViewerWindow::analyzeHit(
+ S32 x, // input
+ S32 y_from_bot, // input
+ LLViewerObject* objectp, // input
+ U32 te_offset, // input
+ LLUUID* hit_object_id_p,// output
+ S32* hit_face_p, // output
+ LLVector3d* hit_pos_p, // output
+ BOOL* hit_land, // output
+ F32* hit_u_coord, // output
+ F32* hit_v_coord) // output
+{
+ // Clean up inputs
+ S32 face = -1;
+
+ if (te_offset != NO_FACE )
+ {
+ face = te_offset;
+ }
+
+ *hit_land = FALSE;
+
+ if (objectp)
+ {
+ if( objectp->getPCode() == LLViewerObject::LL_VO_SURFACE_PATCH )
+ {
+ // Hit land
+ *hit_land = TRUE;
+
+ // put global position into land_pos
+ LLVector3d land_pos;
+ if (mousePointOnLandGlobal(x, y_from_bot, &land_pos))
+ {
+ *hit_object_id_p = LLUUID::null;
+ *hit_face_p = -1;
+
+ // Fudge the land focus a little bit above ground.
+ *hit_pos_p = land_pos + LLVector3d(0.f, 0.f, 0.1f);
+ //llinfos << "DEBUG Hit Land " << *hit_pos_p << llendl;
+ return;
+ }
+ else
+ {
+ //llinfos << "Hit land but couldn't find position" << llendl;
+ // Fall through to "Didn't hit anything"
+ }
+ }
+ else
+ {
+ *hit_object_id_p = objectp->mID;
+ *hit_face_p = face;
+
+ // Hit an object
+ if (objectp->isAvatar())
+ {
+ *hit_pos_p = gAgent.getPosGlobalFromAgent(((LLVOAvatar*)objectp)->mPelvisp->getWorldPosition());
+ }
+ else if (objectp->mDrawable.notNull())
+ {
+ *hit_pos_p = gAgent.getPosGlobalFromAgent(objectp->getRenderPosition());
+ }
+ else
+ {
+ // regular object
+ *hit_pos_p = objectp->getPositionGlobal();
+ }
+
+ if (gPickFaces && face > -1 &&
+ objectp->mDrawable.notNull() && objectp->getPCode() == LL_PCODE_VOLUME &&
+ face < objectp->mDrawable->getNumFaces())
+ {
+ // render red-blue gradient to get 1/256 precision
+ // then render green grid to get final 1/4096 precision
+ S32 scaled_x = llround((F32)x * mDisplayScale.mV[VX]);
+ S32 scaled_y = llround((F32)y_from_bot * mDisplayScale.mV[VY]);
+ const S32 UV_PICK_WIDTH = 41;
+ const S32 UV_PICK_HALF_WIDTH = (UV_PICK_WIDTH - 1) / 2;
+ U8 uv_pick_buffer[UV_PICK_WIDTH * UV_PICK_WIDTH * 4];
+ S32 pick_face = ((LLVOVolume*)objectp)->getAllTEsSame() ? 0 : face;
+ LLFace* facep = objectp->mDrawable->getFace(objectp->getFaceIndexOffset() + pick_face);
+ gCamera->setPerspective(FOR_SELECTION, scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, FALSE);
+ glViewport(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH);
+ gPipeline.renderFaceForUVSelect(facep);
+
+ glReadPixels(scaled_x - UV_PICK_HALF_WIDTH, scaled_y - UV_PICK_HALF_WIDTH, UV_PICK_WIDTH, UV_PICK_WIDTH, GL_RGBA, GL_UNSIGNED_BYTE, uv_pick_buffer);
+ U8* center_pixel = &uv_pick_buffer[4 * ((UV_PICK_WIDTH * UV_PICK_HALF_WIDTH) + UV_PICK_HALF_WIDTH + 1)];
+ *hit_u_coord = (F32)((center_pixel[VGREEN] & 0xf) + (16.f * center_pixel[VRED])) / 4095.f;
+ *hit_v_coord = (F32)((center_pixel[VGREEN] >> 4) + (16.f * center_pixel[VBLUE])) / 4095.f;
+ }
+ else
+ {
+ *hit_u_coord = 0.f;
+ *hit_v_coord = 0.f;
+ }
+
+ //llinfos << "DEBUG Hit Object " << *hit_pos_p << llendl;
+ return;
+ }
+ }
+
+ // Didn't hit anything.
+ *hit_object_id_p = LLUUID::null;
+ *hit_face_p = -1;
+ *hit_pos_p = LLVector3d::zero;
+ *hit_u_coord = 0.f;
+ *hit_v_coord = 0.f;
+ //llinfos << "DEBUG Hit Nothing " << llendl;
+}
+
+
+void LLViewerWindow::requestFastFrame(LLView *view)
+{
+ if (!mPickPending &&
+ mWindow->getSwapMethod() != LLWindow::SWAP_METHOD_UNDEFINED &&
+ gStartupState >= STATE_STARTED &&
+ gSavedSettings.getBOOL("RenderFastUI") &&
+ !gbCapturing)
+ {
+ if (!mFastFrameTimer.getStarted())
+ {
+ // we're double buffered, so when first requesting a fast ui update
+ // we need to render the scene again so that the front and back buffers
+ // are synced
+ mRenderFullFrame = TRUE;
+ }
+ // calculation new expiration time and reset timer
+ F32 expiration;
+ if (mFastFrameTimer.hasExpired())
+ {
+ expiration = FAST_FRAME_INCREMENT;
+ }
+ else
+ {
+ expiration = llmin(MAX_FAST_FRAME_TIME, mFastFrameTimer.getTimeToExpireF32() + FAST_FRAME_INCREMENT);
+ }
+
+ mFastFrameTimer.start();
+ mFastFrameTimer.setTimerExpirySec(expiration);
+
+ LLView::sFastFrameView = view->getRootMostFastFrameView();
+ if (!LLView::sFastFrameView)
+ {
+ LLView::sFastFrameView = view;
+ }
+ }
+}
+
+
+// Returns unit vector relative to camera
+// indicating direction of point on screen x,y
+LLVector3 LLViewerWindow::mouseDirectionGlobal(const S32 x, const S32 y) const
+{
+ // find vertical field of view
+ F32 fov = gCamera->getView();
+
+ // find screen resolution
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ // calculate pixel distance to screen
+ F32 distance = (height / 2.f) / (tan(fov / 2.f));
+
+ // calculate click point relative to middle of screen
+ F32 click_x = x - width / 2.f;
+ F32 click_y = y - height / 2.f;
+
+ // compute mouse vector
+ LLVector3 mouse_vector = distance * gCamera->getAtAxis()
+ - click_x * gCamera->getLeftAxis()
+ + click_y * gCamera->getUpAxis();
+
+ mouse_vector.normVec();
+
+ return mouse_vector;
+}
+
+
+// Returns unit vector relative to camera in camera space
+// indicating direction of point on screen x,y
+LLVector3 LLViewerWindow::mouseDirectionCamera(const S32 x, const S32 y) const
+{
+ // find vertical field of view
+ F32 fov_height = gCamera->getView();
+ F32 fov_width = fov_height * gCamera->getAspect();
+
+ // find screen resolution
+ S32 height = getWindowHeight();
+ S32 width = getWindowWidth();
+
+ // calculate click point relative to middle of screen
+ F32 click_x = (((F32)x / (F32)width) - 0.5f) * fov_width * -1.f;
+ F32 click_y = (((F32)y / (F32)height) - 0.5f) * fov_height;
+
+ // compute mouse vector
+ LLVector3 mouse_vector = LLVector3(0.f, 0.f, -1.f);
+ LLQuaternion mouse_rotate;
+ mouse_rotate.setQuat(click_y, click_x, 0.f);
+
+ mouse_vector = mouse_vector * mouse_rotate;
+ // project to z = -1 plane;
+ mouse_vector = mouse_vector * (-1.f / mouse_vector.mV[VZ]);
+
+ return mouse_vector;
+}
+
+
+
+BOOL LLViewerWindow::mousePointOnPlaneGlobal(LLVector3d& point, const S32 x, const S32 y,
+ const LLVector3d &plane_point_global,
+ const LLVector3 &plane_normal_global)
+{
+ LLVector3d mouse_direction_global_d;
+
+ mouse_direction_global_d.setVec(mouseDirectionGlobal(x,y));
+ LLVector3d plane_normal_global_d;
+ plane_normal_global_d.setVec(plane_normal_global);
+ F64 mouse_look_at_scale = (plane_normal_global_d * (plane_point_global - gAgent.getCameraPositionGlobal()))
+ / (plane_normal_global_d * mouse_direction_global_d);
+ point = gAgent.getCameraPositionGlobal() + mouse_look_at_scale * mouse_direction_global_d;
+
+ return mouse_look_at_scale > 0.0;
+}
+
+
+// Returns global position
+BOOL LLViewerWindow::mousePointOnLandGlobal(const S32 x, const S32 y, LLVector3d *land_position_global)
+{
+ LLVector3 mouse_direction_global = mouseDirectionGlobal(x,y);
+ F32 mouse_dir_scale;
+ BOOL hit_land = FALSE;
+ LLViewerRegion *regionp;
+ F32 land_z;
+ const F32 FIRST_PASS_STEP = 1.0f; // meters
+ const F32 SECOND_PASS_STEP = 0.1f; // meters
+ LLVector3d camera_pos_global;
+
+ camera_pos_global = gAgent.getCameraPositionGlobal();
+ LLVector3d probe_point_global;
+ LLVector3 probe_point_region;
+
+ // walk forwards to find the point
+ for (mouse_dir_scale = FIRST_PASS_STEP; mouse_dir_scale < gAgent.mDrawDistance; mouse_dir_scale += FIRST_PASS_STEP)
+ {
+ LLVector3d mouse_direction_global_d;
+ mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
+ probe_point_global = camera_pos_global + mouse_direction_global_d;
+
+ regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global);
+
+ if (!regionp)
+ {
+ // ...we're outside the world somehow
+ continue;
+ }
+
+ S32 i = (S32) (probe_point_region.mV[VX]/regionp->getLand().getMetersPerGrid());
+ S32 j = (S32) (probe_point_region.mV[VY]/regionp->getLand().getMetersPerGrid());
+ S32 grids_per_edge = (S32) regionp->getLand().mGridsPerEdge;
+ if ((i >= grids_per_edge) || (j >= grids_per_edge))
+ {
+ //llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
+ continue;
+ }
+
+ land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
+
+ //llinfos << "mousePointOnLand initial z " << land_z << llendl;
+
+ if (probe_point_region.mV[VZ] < land_z)
+ {
+ // ...just went under land
+
+ // cout << "under land at " << probe_point << " scale " << mouse_vec_scale << endl;
+
+ hit_land = TRUE;
+ break;
+ }
+ }
+
+
+ if (hit_land)
+ {
+ // Don't go more than one step beyond where we stopped above.
+ // This can't just be "mouse_vec_scale" because floating point error
+ // will stop the loop before the last increment.... X - 1.0 + 0.1 + 0.1 + ... + 0.1 != X
+ F32 stop_mouse_dir_scale = mouse_dir_scale + FIRST_PASS_STEP;
+
+ // take a step backwards, then walk forwards again to refine position
+ for ( mouse_dir_scale -= FIRST_PASS_STEP; mouse_dir_scale <= stop_mouse_dir_scale; mouse_dir_scale += SECOND_PASS_STEP)
+ {
+ LLVector3d mouse_direction_global_d;
+ mouse_direction_global_d.setVec(mouse_direction_global * mouse_dir_scale);
+ probe_point_global = camera_pos_global + mouse_direction_global_d;
+
+ regionp = gWorldPointer->resolveRegionGlobal(probe_point_region, probe_point_global);
+
+ if (!regionp)
+ {
+ // ...we're outside the world somehow
+ continue;
+ }
+
+ /*
+ i = (S32) (local_probe_point.mV[VX]/regionp->getLand().getMetersPerGrid());
+ j = (S32) (local_probe_point.mV[VY]/regionp->getLand().getMetersPerGrid());
+ if ((i >= regionp->getLand().mGridsPerEdge) || (j >= regionp->getLand().mGridsPerEdge))
+ {
+ // llinfos << "LLViewerWindow::mousePointOnLand probe_point is out of region" << llendl;
+ continue;
+ }
+ land_z = regionp->getLand().mSurfaceZ[ i + j * (regionp->getLand().mGridsPerEdge) ];
+ */
+
+ land_z = regionp->getLand().resolveHeightRegion(probe_point_region);
+
+ //llinfos << "mousePointOnLand refine z " << land_z << llendl;
+
+ if (probe_point_region.mV[VZ] < land_z)
+ {
+ // ...just went under land again
+
+ *land_position_global = probe_point_global;
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+// Saves an image to the harddrive as "SnapshotX" where X >= 1.
+BOOL LLViewerWindow::saveImageNumbered(LLImageRaw *raw)
+{
+ if (! raw)
+ {
+ return FALSE;
+ }
+
+ // Get a directory if this is the first time.
+ if (strlen(sSnapshotDir) == 0)
+ {
+ LLString proposed_name( sSnapshotBaseName );
+ proposed_name.append( ".bmp" );
+
+ // pick a directory in which to save
+ LLFilePicker& picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_BMP, proposed_name.c_str()))
+ {
+ // Clicked cancel
+ return FALSE;
+ }
+
+ // Copy the directory + file name
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ // Smash the file extension
+ S32 length = strlen(directory);
+ S32 index = length;
+
+ // Back up over ".bmp"
+ index -= 4;
+ if (index >= 0 && directory[index] == '.')
+ {
+ directory[index] = '\0';
+ }
+ else
+ {
+ index = length;
+ }
+
+ // Find trailing backslash
+ while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0])
+ {
+ index--;
+ }
+
+ // If we found one, truncate the string there
+ if (index >= 0)
+ {
+ if (index + 1 <= length)
+ {
+ strcpy(LLViewerWindow::sSnapshotBaseName, directory + index + 1);
+ }
+
+ index++;
+ directory[index] = '\0';
+ strcpy(LLViewerWindow::sSnapshotDir, directory);
+ }
+ }
+
+ // Look for an unused file name
+ LLString filepath;
+ S32 i = 1;
+ S32 err = 0;
+
+ do
+ {
+ char extension[100];
+ sprintf( extension, "_%.3d.bmp", i );
+ filepath = sSnapshotDir;
+ filepath += sSnapshotBaseName;
+ filepath += extension;
+
+ struct stat stat_info;
+ err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info );
+ i++;
+ }
+ while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
+
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+ LLImageBase::setSizeOverride(TRUE);
+ BOOL success = bmp_image->encode(raw);
+ if( success )
+ {
+ success = bmp_image->save(filepath);
+ }
+ else
+ {
+ llwarns << "Unable to encode bmp snapshot" << llendl;
+ }
+ LLImageBase::setSizeOverride(FALSE);
+
+ return success;
+}
+
+void LLViewerWindow::saveMovieNumbered(void*)
+{
+ if (!gbCapturing)
+ {
+ // Get a directory if this is the first time.
+ if (strlen(sSnapshotDir) == 0)
+ {
+ LLString proposed_name( sMovieBaseName );
+#if LL_DARWIN
+ proposed_name.append( ".mov" );
+#else
+ proposed_name.append( ".avi" );
+#endif
+
+ // pick a directory in which to save
+ LLFilePicker &picker = LLFilePicker::instance();
+ if (!picker.getSaveFile(LLFilePicker::FFSAVE_AVI, proposed_name.c_str()))
+ {
+ // Clicked cancel
+ return;
+ }
+
+ // Copy the directory + file name
+ char directory[LL_MAX_PATH];
+ strcpy(directory, picker.getFirstFile());
+
+ // Smash the file extension
+ S32 length = strlen(directory);
+ S32 index = length;
+
+ // Back up over ".bmp"
+ index -= 4;
+ if (index >= 0 && directory[index] == '.')
+ {
+ directory[index] = '\0';
+ }
+ else
+ {
+ index = length;
+ }
+
+ // Find trailing backslash
+ while (index >= 0 && directory[index] != gDirUtilp->getDirDelimiter()[0])
+ {
+ index--;
+ }
+
+ // If we found one, truncate the string there
+ if (index >= 0)
+ {
+ if (index + 1 <= length)
+ {
+ strcpy(LLViewerWindow::sMovieBaseName, directory + index + 1);
+ }
+
+ index++;
+ directory[index] = '\0';
+ strcpy(LLViewerWindow::sSnapshotDir, directory);
+ }
+ }
+
+ // Look for an unused file name
+ LLString filepath;
+ S32 i = 1;
+ S32 err = 0;
+
+ do
+ {
+ char extension[100];
+#if LL_DARWIN
+ sprintf( extension, "_%.3d.mov", i );
+#else
+ sprintf( extension, "_%.3d.avi", i );
+#endif
+ filepath.assign( sSnapshotDir );
+ filepath.append( sMovieBaseName );
+ filepath.append( extension );
+
+ struct stat stat_info;
+ err = gViewerWindow->mWindow->stat( filepath.c_str(), &stat_info );
+ i++;
+ }
+ while( -1 != err ); // search until the file is not found (i.e., stat() gives an error).
+ S32 x = gViewerWindow->getWindowWidth();
+ S32 y = gViewerWindow->getWindowHeight();
+
+ gbCapturing = TRUE;
+ gMovieMaker.StartCapture((char *)filepath.c_str(), x, y);
+ }
+ else
+ {
+ gMovieMaker.EndCapture();
+ gbCapturing = FALSE;
+ }
+}
+
+static S32 BORDERHEIGHT = 0;
+static S32 BORDERWIDTH = 0;
+
+void LLViewerWindow::movieSize(S32 new_width, S32 new_height)
+{
+ LLCoordScreen size;
+ gViewerWindow->mWindow->getSize(&size);
+ if ( (size.mX != new_width + BORDERWIDTH)
+ ||(size.mY != new_height + BORDERHEIGHT))
+ {
+ S32 x = gViewerWindow->getWindowWidth();
+ S32 y = gViewerWindow->getWindowHeight();
+ BORDERWIDTH = size.mX - x;
+ BORDERHEIGHT = size.mY- y;
+ LLCoordScreen new_size(new_width + BORDERWIDTH,
+ new_height + BORDERHEIGHT);
+ BOOL disable_sync = gSavedSettings.getBOOL("DisableVerticalSync");
+ if (gViewerWindow->mWindow->getFullscreen())
+ {
+ gViewerWindow->changeDisplaySettings(FALSE,
+ new_size,
+ disable_sync,
+ TRUE);
+ }
+ else
+ {
+ gViewerWindow->mWindow->setSize(new_size);
+ }
+ }
+}
+
+BOOL LLViewerWindow::saveSnapshot( const LLString& filepath, S32 image_width, S32 image_height, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
+{
+ llinfos << "Saving snapshot to: " << filepath << llendl;
+
+ LLPointer<LLImageRaw> raw = new LLImageRaw;
+ BOOL success = rawSnapshot(raw, image_width, image_height, TRUE, show_ui, do_rebuild);
+
+ if (success)
+ {
+ LLPointer<LLImageBMP> bmp_image = new LLImageBMP;
+ success = bmp_image->encode(raw);
+ if( success )
+ {
+ success = bmp_image->save(filepath);
+ }
+ else
+ {
+ llwarns << "Unable to encode bmp snapshot" << llendl;
+ }
+ }
+ else
+ {
+ llwarns << "Unable to capture raw snapshot" << llendl;
+ }
+
+ return success;
+}
+
+
+void LLViewerWindow::playSnapshotAnimAndSound()
+{
+ gAgent.sendAnimationRequest(ANIM_AGENT_SNAPSHOT, ANIM_REQUEST_START);
+ send_sound_trigger(LLUUID(gSavedSettings.getString("UISndSnapshot")), 1.0f);
+}
+
+
+// Saves the image from the screen to the specified filename and path.
+BOOL LLViewerWindow::rawSnapshot(LLImageRaw *raw, S32 image_width, S32 image_height,
+ BOOL keep_window_aspect, BOOL show_ui, BOOL do_rebuild, ESnapshotType type)
+{
+ F32 image_aspect_ratio = ((F32)image_width) / ((F32)image_height);
+ F32 window_aspect_ratio = ((F32)getWindowWidth()) / ((F32)getWindowHeight());
+
+ if ((!gWorldPointer) ||
+ (!raw))
+ {
+ return FALSE;
+ }
+
+ // IW 3/5/04 We don'a wan' nunna yer fest frumes har!
+ finishFastFrame();
+
+ // PRE SNAPSHOT
+
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ setCursor(UI_CURSOR_WAIT);
+
+ // Hide all the UI widgets first and draw a frame
+ BOOL prev_draw_ui = gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI);
+
+ if ( prev_draw_ui != show_ui)
+ {
+ LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
+ }
+
+ BOOL hide_hud = !gSavedSettings.getBOOL("RenderHUDInSnapshot") && gPipeline.hasRenderType(LLPipeline::RENDER_TYPE_HUD);
+ if (hide_hud)
+ {
+ LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+ }
+
+ // Copy screen to a buffer
+ // crop sides or top and bottom, if taking a snapshot of different aspect ratio
+ // from window
+ S32 snapshot_width = mWindowRect.getWidth();
+ S32 snapshot_height = mWindowRect.getHeight();
+ if (!keep_window_aspect)
+ {
+ if (image_aspect_ratio > window_aspect_ratio)
+ {
+ snapshot_height = llround((F32)snapshot_width / image_aspect_ratio);
+ }
+ else if (image_aspect_ratio < window_aspect_ratio)
+ {
+ snapshot_width = llround((F32)snapshot_height * image_aspect_ratio);
+ }
+ }
+
+ F32 scale_factor = llmax(1.f, (F32)image_width / snapshot_width, (F32)image_height / snapshot_height);
+ raw->resize(llfloor(snapshot_width*scale_factor), llfloor(snapshot_height *scale_factor), type == SNAPSHOT_TYPE_DEPTH ? 4 : 3);
+
+ BOOL high_res = scale_factor > 1.f;
+ if (high_res)
+ {
+ send_agent_pause();
+ //rescale fonts
+ initFonts(scale_factor);
+ LLHUDText::reshape();
+ }
+
+ // SNAPSHOT
+ S32 window_width = mWindowRect.getWidth();
+ S32 window_height = mWindowRect.getHeight();
+ S32 buffer_x_offset = llfloor(((window_width - snapshot_width) * scale_factor) / 2.f);
+ S32 buffer_y_offset = llfloor(((window_height - snapshot_height) * scale_factor) / 2.f);
+
+ S32 output_buffer_offset_y = 0;
+
+ F32 depth_conversion_factor_1 = (gCamera->getFar() + gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear());
+ F32 depth_conversion_factor_2 = (gCamera->getFar() - gCamera->getNear()) / (2.f * gCamera->getFar() * gCamera->getNear());
+
+ for (int subimage_y = 0; subimage_y < scale_factor; ++subimage_y)
+ {
+ S32 subimage_y_offset = llclamp(buffer_y_offset - (subimage_y * window_height), 0, window_height);;
+ // handle fractional columns
+ U32 read_height = llmax(0, (window_height - subimage_y_offset) -
+ llmax(0, (window_height * (subimage_y + 1)) - (buffer_y_offset + raw->getHeight())));
+
+ S32 output_buffer_offset_x = 0;
+ for (int subimage_x = 0; subimage_x < scale_factor; ++subimage_x)
+ {
+ gDisplaySwapBuffers = FALSE;
+ if (type == SNAPSHOT_TYPE_OBJECT_ID)
+ {
+ glClearColor(0.f, 0.f, 0.f, 1.f);
+ glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+
+ gCamera->setZoomParameters(scale_factor, subimage_x+(subimage_y*llceil(scale_factor)));
+ setup3DRender();
+ setupViewport();
+ BOOL first_time_through = (subimage_x + subimage_y == 0);
+ gPickTransparent = FALSE;
+ gPickAlphaThreshold = 0.1f;
+ gObjectList.renderObjectsForSelect(*gCamera, FALSE, !first_time_through);
+ }
+ else
+ {
+ display(do_rebuild, scale_factor, subimage_x+(subimage_y*llceil(scale_factor)));
+ }
+ glFlush();
+ S32 subimage_x_offset = llclamp(buffer_x_offset - (subimage_x * window_width), 0, window_width);
+ // handle fractional rows
+ U32 read_width = llmax(0, (window_width - subimage_x_offset) -
+ llmax(0, (window_width * (subimage_x + 1)) - (buffer_x_offset + raw->getWidth())));
+ for(U32 out_y = 0; out_y < read_height ; out_y++)
+ {
+ if (type == SNAPSHOT_TYPE_OBJECT_ID || type == SNAPSHOT_TYPE_COLOR)
+ {
+ glReadPixels(
+ subimage_x_offset, out_y + subimage_y_offset,
+ read_width, 1,
+ GL_RGB, GL_UNSIGNED_BYTE,
+ raw->getData() + // current output pixel is beginning of buffer...
+ (
+ (out_y * (raw->getWidth())) // ...plus iterated y...
+ + (window_width * subimage_x) // ...plus subimage start in x...
+ + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y...
+ - output_buffer_offset_x // ...minus buffer padding x...
+ - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y...
+ ) * 3 // times 3 bytes per pixel
+ );
+ }
+ else // SNAPSHOT_TYPE_DEPTH
+ {
+ S32 output_buffer_offset = (
+ (out_y * (raw->getWidth())) // ...plus iterated y...
+ + (window_width * subimage_x) // ...plus subimage start in x...
+ + (raw->getWidth() * window_height * subimage_y) // ...plus subimage start in y...
+ - output_buffer_offset_x // ...minus buffer padding x...
+ - (output_buffer_offset_y * (raw->getWidth())) // ...minus buffer padding y...
+ ) * 4; // times 4 bytes per pixel
+
+ glReadPixels(
+ subimage_x_offset, out_y + subimage_y_offset,
+ read_width, 1,
+ GL_DEPTH_COMPONENT, GL_FLOAT,
+ raw->getData() + output_buffer_offset// current output pixel is beginning of buffer...
+ );
+
+ for (S32 i = output_buffer_offset; i < output_buffer_offset + (S32)read_width * 4; i += 4)
+ {
+ F32 depth_float = *(F32*)(raw->getData() + i);
+
+ F32 linear_depth_float = 1.f / (depth_conversion_factor_1 - (depth_float * depth_conversion_factor_2));
+ U8 depth_byte = F32_to_U8(linear_depth_float, gCamera->getNear(), gCamera->getFar());
+ *(raw->getData() + i + 0) = depth_byte;
+ *(raw->getData() + i + 1) = depth_byte;
+ *(raw->getData() + i + 2) = depth_byte;
+ *(raw->getData() + i + 3) = 255;
+ }
+ }
+ }
+ output_buffer_offset_x += subimage_x_offset;
+ stop_glerror();
+ }
+ output_buffer_offset_y += subimage_y_offset;
+ }
+
+ // POST SNAPSHOT
+ if (!gPipeline.hasRenderDebugFeatureMask(LLPipeline::RENDER_DEBUG_FEATURE_UI))
+ {
+ LLPipeline::toggleRenderDebugFeature((void*)LLPipeline::RENDER_DEBUG_FEATURE_UI);
+ }
+
+ if (hide_hud)
+ {
+ LLPipeline::toggleRenderType((void*)LLPipeline::RENDER_TYPE_HUD);
+ }
+
+ if (high_res)
+ {
+ initFonts(1.f);
+ LLHUDText::reshape();
+ }
+
+ gDisplaySwapBuffers = TRUE;
+
+ // Pre-pad image to number of pixels such that the line length is a multiple of 4 bytes (for BMP encoding)
+ // Note: this formula depends on the number of components being 3. Not obvious, but it's correct.
+ image_width += (image_width * (type == SNAPSHOT_TYPE_DEPTH ? 4 : 3)) % 4;
+
+ // Resize image
+ raw->scale( image_width, image_height );
+
+ setCursor(UI_CURSOR_ARROW);
+
+ if (do_rebuild)
+ {
+ // If we had to do a rebuild, that means that the lists of drawables to be rendered
+ // was empty before we started.
+ // Need to reset these, otherwise we call state sort on it again when render gets called the next time
+ // and we stand a good chance of crashing on rebuild because the render drawable arrays have multiple copies of
+ // objects on them.
+ gPipeline.resetDrawOrders();
+ }
+
+ if (high_res)
+ {
+ send_agent_resume();
+ }
+
+ return TRUE;
+}
+
+void LLViewerWindow::destroyWindow()
+{
+ if (mWindow)
+ {
+ LLWindowManager::destroyWindow(mWindow);
+ }
+ mWindow = NULL;
+}
+
+
+void LLViewerWindow::drawMouselookInstructions()
+{
+ // Draw instructions for mouselook ("Press ESC to leave Mouselook" in a box at the top of the screen.)
+ const char* instructions = "Press ESC to leave Mouselook.";
+ const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
+
+ const S32 INSTRUCTIONS_PAD = 5;
+ LLRect instructions_rect;
+ instructions_rect.setLeftTopAndSize(
+ INSTRUCTIONS_PAD,
+ gViewerWindow->getWindowHeight() - INSTRUCTIONS_PAD,
+ font->getWidth( instructions ) + 2 * INSTRUCTIONS_PAD,
+ llround(font->getLineHeight() + 2 * INSTRUCTIONS_PAD));
+
+ {
+ LLGLSNoTexture gls_no_texture;
+ glColor4f( 0.9f, 0.9f, 0.9f, 1.0f );
+ gl_rect_2d( instructions_rect );
+ }
+
+ font->renderUTF8(
+ instructions, 0,
+ instructions_rect.mLeft + INSTRUCTIONS_PAD,
+ instructions_rect.mTop - INSTRUCTIONS_PAD,
+ LLColor4( 0.0f, 0.0f, 0.0f, 1.f ),
+ LLFontGL::LEFT, LLFontGL::TOP);
+}
+
+
+// These functions are here only because LLViewerWindow used to do the work that gFocusMgr does now.
+// They let other objects continue to work without change.
+
+void LLViewerWindow::setKeyboardFocus(LLUICtrl* new_focus,void (*on_focus_lost)(LLUICtrl* old_focus))
+{
+ gFocusMgr.setKeyboardFocus( new_focus, on_focus_lost );
+}
+
+LLUICtrl* LLViewerWindow::getKeyboardFocus()
+{
+ return gFocusMgr.getKeyboardFocus();
+}
+
+BOOL LLViewerWindow::hasKeyboardFocus(const LLUICtrl* possible_focus) const
+{
+ return possible_focus == gFocusMgr.getKeyboardFocus();
+}
+
+BOOL LLViewerWindow::childHasKeyboardFocus(const LLView* parent) const
+{
+ return gFocusMgr.childHasKeyboardFocus( parent );
+}
+
+void LLViewerWindow::setMouseCapture(LLMouseHandler* new_captor,void (*on_capture_lost)(LLMouseHandler* old_captor))
+{
+ gFocusMgr.setMouseCapture( new_captor, on_capture_lost );
+}
+
+LLMouseHandler* LLViewerWindow::getMouseCaptor() const
+{
+ return gFocusMgr.getMouseCapture();
+}
+
+BOOL LLViewerWindow::hasMouseCapture(const LLMouseHandler* possible_captor) const
+{
+ return possible_captor == gFocusMgr.getMouseCapture();
+}
+
+S32 LLViewerWindow::getWindowHeight() const
+{
+ return mVirtualWindowRect.getHeight();
+}
+
+S32 LLViewerWindow::getWindowWidth() const
+{
+ return mVirtualWindowRect.getWidth();
+}
+
+S32 LLViewerWindow::getWindowDisplayHeight() const
+{
+ return mWindowRect.getHeight();
+}
+
+S32 LLViewerWindow::getWindowDisplayWidth() const
+{
+ return mWindowRect.getWidth();
+}
+
+LLView* LLViewerWindow::getTopView() const
+{
+ return gFocusMgr.getTopView();
+}
+
+BOOL LLViewerWindow::hasTopView(LLView* view) const
+{
+ return view == gFocusMgr.getTopView();
+}
+
+void LLViewerWindow::setTopView(LLView* new_top,void (*on_top_lost)(LLView* old_top))
+{
+ gFocusMgr.setTopView( new_top, on_top_lost );
+}
+
+void LLViewerWindow::setupViewport(S32 x_offset, S32 y_offset)
+{
+ glViewport(x_offset, y_offset, mWindowRect.getWidth(), mWindowRect.getHeight());
+}
+
+void LLViewerWindow::setup3DRender()
+{
+ gCamera->setPerspective(NOT_FOR_SELECTION, 0, 0, mWindowRect.getWidth(), mWindowRect.getHeight(), FALSE, gCamera->getNear(), MAX_FAR_PLANE);
+}
+
+void LLViewerWindow::setup2DRender()
+{
+ gl_state_for_2d(mWindowRect.getWidth(), mWindowRect.getHeight());
+}
+
+// Could cache the pointer from the last hitObjectOrLand here.
+LLViewerObject *LLViewerWindow::lastObjectHit()
+{
+ return gObjectList.findObject( gLastHitObjectID );
+}
+
+const LLVector3d& LLViewerWindow::lastObjectHitOffset()
+{
+ return gLastHitObjectOffset;
+}
+
+// Could cache the pointer from the last hitObjectOrLand here.
+LLViewerObject *LLViewerWindow::lastNonFloraObjectHit()
+{
+ return gObjectList.findObject( gLastHitNonFloraObjectID );
+}
+
+const LLVector3d& LLViewerWindow::lastNonFloraObjectHitOffset()
+{
+ return gLastHitNonFloraObjectOffset;
+}
+
+
+void LLViewerWindow::setShowProgress(const BOOL show)
+{
+ if (mProgressView)
+ {
+ mProgressView->setVisible(show);
+ }
+}
+
+BOOL LLViewerWindow::getShowProgress() const
+{
+ return (mProgressView && mProgressView->getVisible());
+}
+
+
+void LLViewerWindow::moveProgressViewToFront()
+{
+ if( mProgressView && mRootView )
+ {
+ mRootView->removeChild( mProgressView );
+ mRootView->addChild( mProgressView );
+ }
+}
+
+void LLViewerWindow::setProgressString(const LLString& string)
+{
+ if (mProgressView)
+ {
+ mProgressView->setText(string);
+ }
+}
+
+void LLViewerWindow::setProgressMessage(const LLString& msg)
+{
+ if(mProgressView)
+ {
+ mProgressView->setMessage(msg);
+ }
+}
+
+void LLViewerWindow::setProgressPercent(const F32 percent)
+{
+ if (mProgressView)
+ {
+ mProgressView->setPercent(percent);
+ }
+}
+
+void LLViewerWindow::setProgressCancelButtonVisible( BOOL b, const LLString& label )
+{
+ if (mProgressView)
+ {
+ mProgressView->setCancelButtonVisible( b, label );
+ }
+}
+
+
+LLProgressView *LLViewerWindow::getProgressView() const
+{
+ return mProgressView;
+}
+
+void LLViewerWindow::dumpState()
+{
+ llinfos << "LLViewerWindow Active " << S32(mActive) << llendl;
+ llinfos << "mWindow visible " << S32(mWindow->getVisible())
+ << " minimized " << S32(mWindow->getMinimized())
+ << llendl;
+}
+
+void LLViewerWindow::stopGL(BOOL save_state)
+{
+ if (!gGLManager.mIsDisabled)
+ {
+ llinfos << "Shutting down GL..." << llendl;
+ gSky.destroyGL();
+ stop_glerror();
+
+ gImageList.destroyGL(save_state);
+ stop_glerror();
+
+ gBumpImageList.destroyGL();
+ stop_glerror();
+
+ LLFontGL::destroyGL();
+ stop_glerror();
+
+ LLVOAvatar::destroyGL();
+ stop_glerror();
+
+ LLDynamicTexture::destroyGL();
+ stop_glerror();
+
+ if(gParcelMgr) gParcelMgr->destroyGL();
+
+ gPipeline.destroyGL();
+
+ gCone.cleanupGL();
+ gBox.cleanupGL();
+ gSphere.cleanupGL();
+ gCylinder.cleanupGL();
+
+ gGLManager.mIsDisabled = TRUE;
+ stop_glerror();
+
+ llinfos << "Remaining allocated texture memory: " << LLImageGL::sGlobalTextureMemory << " bytes" << llendl;
+ }
+}
+
+void LLViewerWindow::restoreGL(const LLString& progress_message)
+{
+ if (gGLManager.mIsDisabled)
+ {
+ llinfos << "Restoring GL..." << llendl;
+ gGLManager.mIsDisabled = FALSE;
+
+ // for future support of non-square pixels, and fonts that are properly stretched
+ //LLFontGL::destroyDefaultFonts();
+ initFonts();
+ initGLDefaults();
+ LLGLState::restoreGL();
+ gSky.restoreGL();
+ gPipeline.restoreGL();
+ LLDrawPoolWater::restoreGL();
+ LLManipTranslate::restoreGL();
+ gImageList.restoreGL();
+ gBumpImageList.restoreGL();
+ gPipeline.setUseAGP(gSavedSettings.getBOOL("RenderUseAGP"));
+ LLDynamicTexture::restoreGL();
+ LLVOAvatar::restoreGL();
+ if (gParcelMgr) gParcelMgr->restoreGL();
+
+ if (gFloaterCustomize && gFloaterCustomize->getVisible())
+ {
+ LLVisualParamHint::requestHintUpdates();
+ }
+
+ if (!progress_message.empty())
+ {
+ gRestoreGLTimer.reset();
+ gRestoreGL = TRUE;
+ setShowProgress(TRUE);
+ setProgressString(progress_message);
+ }
+ llinfos << "...Restoring GL done" << llendl;
+#if LL_WINDOWS
+ if (SetUnhandledExceptionFilter(LLWinDebug::handleException) != LLWinDebug::handleException)
+ {
+ llwarns << " Someone took over my exception handler (post restoreGL)!" << llendl;
+ }
+#endif
+
+ }
+}
+
+void LLViewerWindow::initFonts(F32 zoom_factor)
+{
+ LLFontGL::destroyGL();
+ LLFontGL::initDefaultFonts( gSavedSettings.getF32("FontScreenDPI"),
+ mDisplayScale.mV[VX] * zoom_factor,
+ mDisplayScale.mV[VY] * zoom_factor,
+ gSavedSettings.getString("FontMonospace"),
+ gSavedSettings.getF32("FontSizeMonospace"),
+ gSavedSettings.getString("FontSansSerif"),
+ gSavedSettings.getString("FontSansSerifFallback"),
+ gSavedSettings.getF32("FontSansSerifFallbackScale"),
+ gSavedSettings.getF32("FontSizeSmall"),
+ gSavedSettings.getF32("FontSizeMedium"),
+ gSavedSettings.getF32("FontSizeLarge"),
+ gSavedSettings.getF32("FontSizeHuge"),
+ gSavedSettings.getString("FontSansSerifBold"),
+ gSavedSettings.getF32("FontSizeMedium"),
+ gDirUtilp->getAppRODataDir()
+ );
+}
+void LLViewerWindow::toggleFullscreen(BOOL show_progress)
+{
+ if (mWindow)
+ {
+ mWantFullscreen = mWindow->getFullscreen() ? FALSE : TRUE;
+ mShowFullscreenProgress = show_progress;
+ }
+}
+
+void LLViewerWindow::getTargetWindow(BOOL& fullscreen, S32& width, S32& height) const
+{
+ fullscreen = mWantFullscreen;
+
+ if (gViewerWindow->mWindow
+ && gViewerWindow->mWindow->getFullscreen() == mWantFullscreen)
+ {
+ width = gViewerWindow->getWindowDisplayWidth();
+ height = gViewerWindow->getWindowDisplayHeight();
+ }
+ else if (mWantFullscreen)
+ {
+ width = gSavedSettings.getS32("FullScreenWidth");
+ height = gSavedSettings.getS32("FullScreenHeight");
+ }
+ else
+ {
+ width = gSavedSettings.getS32("WindowWidth");
+ height = gSavedSettings.getS32("WindowHeight");
+ }
+}
+
+
+BOOL LLViewerWindow::checkSettings()
+{
+ BOOL is_fullscreen = gViewerWindow->mWindow->getFullscreen();
+ if (is_fullscreen && !mWantFullscreen)
+ {
+ gViewerWindow->changeDisplaySettings(FALSE,
+ LLCoordScreen(gSavedSettings.getS32("WindowWidth"),
+ gSavedSettings.getS32("WindowHeight")),
+ TRUE,
+ mShowFullscreenProgress);
+ return TRUE;
+ }
+ else if (!is_fullscreen && mWantFullscreen)
+ {
+ if (!LLStartUp::canGoFullscreen())
+ {
+ return FALSE;
+ }
+
+ gViewerWindow->changeDisplaySettings(TRUE,
+ LLCoordScreen(gSavedSettings.getS32("FullScreenWidth"),
+ gSavedSettings.getS32("FullScreenHeight")),
+ gSavedSettings.getBOOL("DisableVerticalSync"),
+ mShowFullscreenProgress);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void LLViewerWindow::restartDisplay(BOOL show_progress_bar)
+{
+ llinfos << "Restaring GL" << llendl;
+ stopGL();
+ if (show_progress_bar)
+ {
+ restoreGL("Changing Resolution...");
+ }
+ else
+ {
+ restoreGL();
+ }
+}
+
+BOOL LLViewerWindow::changeDisplaySettings(BOOL fullscreen, LLCoordScreen size, BOOL disable_vsync, BOOL show_progress_bar)
+{
+ BOOL was_maximized = gSavedSettings.getBOOL("WindowMaximized");
+ mWantFullscreen = fullscreen;
+ mShowFullscreenProgress = show_progress_bar;
+ gSavedSettings.setBOOL("FullScreen", mWantFullscreen);
+
+ BOOL old_fullscreen = mWindow->getFullscreen();
+ if (!old_fullscreen && fullscreen && !LLStartUp::canGoFullscreen())
+ {
+ // we can't do this now, so do it later
+
+ gSavedSettings.setS32("FullScreenWidth", size.mX);
+ gSavedSettings.setS32("FullScreenHeight", size.mY);
+ //gSavedSettings.setBOOL("DisableVerticalSync", disable_vsync);
+
+ return TRUE; // a lie..., because we'll get to it later
+ }
+
+ // going from windowed to windowed
+ if (!old_fullscreen && !fullscreen)
+ {
+ // if not maximized, use the request size
+ if (!mWindow->getMaximized())
+ {
+ mWindow->setSize(size);
+ }
+ return TRUE;
+ }
+
+ // Close floaters that don't handle settings change
+ LLFloaterSnapshot::hide(0);
+
+ BOOL result_first_try = FALSE;
+ BOOL result_second_try = FALSE;
+
+ LLUICtrl* keyboard_focus = gFocusMgr.getKeyboardFocus();
+ send_agent_pause();
+ llinfos << "Stopping GL during changeDisplaySettings" << llendl;
+ stopGL();
+ mIgnoreActivate = TRUE;
+ LLCoordScreen old_size;
+ LLCoordScreen old_pos;
+ mWindow->getSize(&old_size);
+ BOOL got_position = mWindow->getPosition(&old_pos);
+
+ if (!old_fullscreen && fullscreen && got_position)
+ {
+ // switching from windowed to fullscreen, so save window position
+ gSavedSettings.setS32("WindowX", old_pos.mX);
+ gSavedSettings.setS32("WindowY", old_pos.mY);
+ }
+
+ result_first_try = mWindow->switchContext(fullscreen, size, disable_vsync);
+ if (!result_first_try)
+ {
+ // try to switch back
+ result_second_try = mWindow->switchContext(old_fullscreen, old_size, disable_vsync);
+
+ if (!result_second_try)
+ {
+ // we are stuck...try once again with a minimal resolution?
+ send_agent_resume();
+ mIgnoreActivate = FALSE;
+ return FALSE;
+ }
+ }
+ send_agent_resume();
+
+ llinfos << "Restoring GL during resolution change" << llendl;
+ if (show_progress_bar)
+ {
+ restoreGL("Changing Resolution...");
+ }
+ else
+ {
+ restoreGL();
+ }
+
+ if (!result_first_try)
+ {
+ LLStringBase<char>::format_map_t args;
+ args["[RESX]"] = llformat("%d",size.mX);
+ args["[RESY]"] = llformat("%d",size.mY);
+ alertXml("ResolutionSwitchFail", args);
+ size = old_size; // for reshape below
+ }
+
+ BOOL success = result_first_try || result_second_try;
+ if (success)
+ {
+#if LL_WINDOWS
+ // Only trigger a reshape after switching to fullscreen; otherwise rely on the windows callback
+ // (otherwise size is wrong; this is the entire window size, reshape wants the visible window size)
+ if (fullscreen)
+#endif
+ {
+ reshape(size.mX, size.mY);
+ }
+ }
+
+ if (!mWindow->getFullscreen() && success)
+ {
+ // maximize window if was maximized, else reposition
+ if (was_maximized)
+ {
+ mWindow->maximize();
+ }
+ else
+ {
+ S32 windowX = gSavedSettings.getS32("WindowX");
+ S32 windowY = gSavedSettings.getS32("WindowY");
+
+ mWindow->setPosition(LLCoordScreen ( windowX, windowY ) );
+ }
+ }
+
+ mIgnoreActivate = FALSE;
+ gFocusMgr.setKeyboardFocus(keyboard_focus, NULL);
+ mWantFullscreen = mWindow->getFullscreen();
+ mShowFullscreenProgress = FALSE;
+
+ return success;
+}
+
+
+F32 LLViewerWindow::getDisplayAspectRatio() const
+{
+ if (mWindow->getFullscreen())
+ {
+ if (gSavedSettings.getBOOL("FullScreenAutoDetectAspectRatio"))
+ {
+ return mWindow->getNativeAspectRatio();
+ }
+ else
+ {
+ return gSavedSettings.getF32("FullScreenAspectRatio");
+ }
+ }
+ else
+ {
+ return mWindow->getNativeAspectRatio();
+ }
+}
+
+
+void LLViewerWindow::drawPickBuffer() const
+{
+ if (mPickBuffer)
+ {
+ LLGLDisable no_blend(GL_BLEND);
+ LLGLDisable no_alpha_test(GL_ALPHA_TEST);
+ LLGLSNoTexture no_texture;
+ glPixelZoom(10.f, 10.f);
+ glRasterPos2f(((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f),
+ ((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
+ glDrawPixels(PICK_DIAMETER, PICK_DIAMETER, GL_RGBA, GL_UNSIGNED_BYTE, mPickBuffer);
+ glPixelZoom(1.f, 1.f);
+ glColor4fv(LLColor4::white.mV);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+ FALSE);
+ gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f + 10.f));
+ gl_line_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] - (F32)(PICK_HALF_WIDTH)),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f + 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + 10.f));
+ glTranslatef(10.f, 10.f, 0.f);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX]),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_DIAMETER) * 10.f),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_DIAMETER) * 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY]),
+ FALSE);
+ gl_rect_2d(llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX)* 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY + 1) * 10.f),
+ llround((F32)mPickPoint.mX * mDisplayScale.mV[VX] + (F32)(PICK_HALF_WIDTH + mPickOffset.mX + 1) * 10.f),
+ llround((F32)mPickPoint.mY * mDisplayScale.mV[VY] + (F32)(PICK_HALF_WIDTH + mPickOffset.mY) * 10.f),
+ FALSE);
+ glPopMatrix();
+ }
+}
+
+void LLViewerWindow::calcDisplayScale()
+{
+ F32 ui_scale_factor = gSavedSettings.getF32("UIScaleFactor");
+ LLVector2 display_scale;
+ display_scale.setVec(llmax(1.f / mWindow->getPixelAspectRatio(), 1.f), llmax(mWindow->getPixelAspectRatio(), 1.f));
+ F32 height_normalization = gSavedSettings.getBOOL("UIAutoScale") ? ((F32)mWindowRect.getHeight() / display_scale.mV[VY]) / 768.f : 1.f;
+ if(mWindow->getFullscreen())
+ {
+ display_scale *= (ui_scale_factor * height_normalization);
+ }
+ else
+ {
+ display_scale *= ui_scale_factor;
+ }
+
+ // limit minimum display scale
+ if (display_scale.mV[VX] < MIN_DISPLAY_SCALE || display_scale.mV[VY] < MIN_DISPLAY_SCALE)
+ {
+ display_scale *= MIN_DISPLAY_SCALE / llmin(display_scale.mV[VX], display_scale.mV[VY]);
+ }
+
+ if (mWindow->getFullscreen())
+ {
+ display_scale.mV[0] = llround(display_scale.mV[0], 2.0f/(F32) mWindowRect.getWidth());
+ display_scale.mV[1] = llround(display_scale.mV[1], 2.0f/(F32) mWindowRect.getHeight());
+ }
+
+ if (display_scale != mDisplayScale)
+ {
+ llinfos << "Setting display scale to " << display_scale << llendl;
+
+ mDisplayScale = display_scale;
+ // Init default fonts
+ initFonts();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// static
+bool LLViewerWindow::alertCallback(S32 modal)
+{
+ if (gNoRender)
+ {
+ return false;
+ }
+ else
+ {
+// if (modal) // we really always want to take you out of mouselook
+ {
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+ }
+ return true;
+ }
+}
+
+LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename,
+ LLAlertDialog::alert_callback_t callback, void* user_data)
+{
+ LLString::format_map_t args;
+ return alertXml( xml_filename, args, callback, user_data );
+}
+
+LLAlertDialog* LLViewerWindow::alertXml(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data)
+{
+ if (gNoRender)
+ {
+ llinfos << "Alert: " << xml_filename << llendl;
+ if (callback)
+ {
+ callback(-1, user_data);
+ }
+ return NULL;
+ }
+
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // Note: object adds, removes, and destroys itself.
+ return LLAlertDialog::showXml( xml_filename, args, callback, user_data );
+}
+
+LLAlertDialog* LLViewerWindow::alertXmlEditText(const std::string& xml_filename, const LLString::format_map_t& args,
+ LLAlertDialog::alert_callback_t callback, void* user_data,
+ LLAlertDialog::alert_text_callback_t text_callback, void *text_data,
+ const LLString::format_map_t& edit_args, BOOL draw_asterixes)
+{
+ if (gNoRender)
+ {
+ llinfos << "Alert: " << xml_filename << llendl;
+ if (callback)
+ {
+ callback(-1, user_data);
+ }
+ return NULL;
+ }
+
+ // If we're in mouselook, the mouse is hidden and so the user can't click
+ // the dialog buttons. In that case, change to First Person instead.
+ if( gAgent.cameraMouselook() )
+ {
+ gAgent.changeCameraToDefault();
+ }
+
+ // Note: object adds, removes, and destroys itself.
+ LLAlertDialog* alert = LLAlertDialog::createXml( xml_filename, args, callback, user_data );
+ if (alert)
+ {
+ if (text_callback)
+ {
+ alert->setEditTextCallback(text_callback, text_data);
+ }
+ alert->setEditTextArgs(edit_args);
+ alert->setDrawAsterixes(draw_asterixes);
+ alert->show();
+ }
+ return alert;
+}
+
+LLBottomPanel::LLBottomPanel(const LLString &name, const LLRect &rect) :
+ LLPanel(name, rect, FALSE),
+ mIndicator(NULL)
+{
+ // bottom panel is focus root, so Tab moves through the toolbar and button bar, and overlay
+ setFocusRoot(TRUE);
+ // don't capture mouse clicks that don't hit a child
+ setMouseOpaque(FALSE);
+ setFollows(FOLLOWS_LEFT | FOLLOWS_RIGHT | FOLLOWS_BOTTOM);
+ setIsChrome(TRUE);
+}
+
+void LLBottomPanel::setFocusIndicator(LLView * indicator)
+{
+ mIndicator = indicator;
+}
+
+void LLBottomPanel::draw()
+{
+ if(mIndicator)
+ {
+ BOOL hasFocus = gFocusMgr.childHasKeyboardFocus(this);
+ mIndicator->setVisible(hasFocus);
+ mIndicator->setEnabled(hasFocus);
+ }
+ LLPanel::draw();
+}