/** * @file lldraghandle.cpp * @brief LLDragHandle base class * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ // A widget for dragging a view around the screen using the mouse. #include "linden_common.h" #include "lldraghandle.h" #include "llmath.h" //#include "llviewerwindow.h" #include "llui.h" #include "llmenugl.h" #include "lltextbox.h" #include "llcontrol.h" #include "llresmgr.h" #include "llfontgl.h" #include "llwindow.h" #include "llfocusmgr.h" const S32 LEADING_PAD = 5; const S32 TITLE_PAD = 8; const S32 BORDER_PAD = 1; const S32 LEFT_PAD = BORDER_PAD + TITLE_PAD + LEADING_PAD; const S32 RIGHT_PAD = BORDER_PAD + 32; // HACK: space for close btn and minimize btn S32 LLDragHandle::sSnapMargin = 5; LLDragHandle::LLDragHandle( const LLString& name, const LLRect& rect, const LLString& title ) : LLView( name, rect, TRUE ), mDragLastScreenX( 0 ), mDragLastScreenY( 0 ), mLastMouseScreenX( 0 ), mLastMouseScreenY( 0 ), mDragHighlightColor( LLUI::sColorsGroup->getColor( "DefaultHighlightLight" ) ), mDragShadowColor( LLUI::sColorsGroup->getColor( "DefaultShadowDark" ) ), mTitleBox( NULL ), mMaxTitleWidth( 0 ), mForeground( TRUE ) { sSnapMargin = LLUI::sConfigGroup->getS32("SnapMargin"); setSaveToXML(false); } void LLDragHandle::setTitleVisible(BOOL visible) { mTitleBox->setVisible(visible); } LLDragHandleTop::LLDragHandleTop(const LLString& name, const LLRect &rect, const LLString& title) : LLDragHandle(name, rect, title) { setFollowsAll(); setTitle( title ); } EWidgetType LLDragHandleTop::getWidgetType() const { return WIDGET_TYPE_DRAG_HANDLE_TOP; } LLString LLDragHandleTop::getWidgetTag() const { return LL_DRAG_HANDLE_TOP_TAG; } LLDragHandleLeft::LLDragHandleLeft(const LLString& name, const LLRect &rect, const LLString& title) : LLDragHandle(name, rect, title) { setFollowsAll(); setTitle( title ); } EWidgetType LLDragHandleLeft::getWidgetType() const { return WIDGET_TYPE_DRAG_HANDLE_LEFT; } LLString LLDragHandleLeft::getWidgetTag() const { return LL_DRAG_HANDLE_LEFT_TAG; } void LLDragHandleTop::setTitle(const LLString& title) { if( mTitleBox ) { removeChild(mTitleBox); delete mTitleBox; } LLString trimmed_title = title; LLString::trim(trimmed_title); const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF ); mTitleBox = new LLTextBox( "Drag Handle Title", mRect, trimmed_title, font ); mTitleBox->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT | FOLLOWS_RIGHT); mTitleBox->setFontStyle(LLFontGL::DROP_SHADOW_SOFT); reshapeTitleBox(); // allow empty titles, as default behavior replaces them with title box name if (trimmed_title.empty()) { mTitleBox->setText(LLString::null); } addChild( mTitleBox ); } const LLString& LLDragHandleTop::getTitle() const { return mTitleBox->getText(); } void LLDragHandleLeft::setTitle(const LLString& ) { if( mTitleBox ) { removeChild(mTitleBox); delete mTitleBox; } mTitleBox = NULL; /* no title on left edge */ } const LLString& LLDragHandleLeft::getTitle() const { return LLString::null; } void LLDragHandleTop::draw() { /* Disable lines. Can drag anywhere in most windows. JC if( getVisible() && mEnabled && mForeground) { const S32 BORDER_PAD = 2; const S32 HPAD = 2; const S32 VPAD = 2; S32 left = BORDER_PAD + HPAD; S32 top = mRect.getHeight() - 2 * VPAD; S32 right = mRect.getWidth() - HPAD; // S32 bottom = VPAD; // draw lines for drag areas const S32 LINE_SPACING = (DRAG_HANDLE_HEIGHT - 2 * VPAD) / 4; S32 line = top - LINE_SPACING; LLRect title_rect = mTitleBox->getRect(); S32 title_right = title_rect.mLeft + mTitleWidth; BOOL show_right_side = title_right < mRect.getWidth(); for( S32 i=0; i<4; i++ ) { gl_line_2d(left, line+1, title_rect.mLeft - LEADING_PAD, line+1, mDragHighlightColor); if( show_right_side ) { gl_line_2d(title_right, line+1, right, line+1, mDragHighlightColor); } gl_line_2d(left, line, title_rect.mLeft - LEADING_PAD, line, mDragShadowColor); if( show_right_side ) { gl_line_2d(title_right, line, right, line, mDragShadowColor); } line -= LINE_SPACING; } } */ // Colorize the text to match the frontmost state if (mTitleBox) { mTitleBox->setEnabled(mForeground); } LLView::draw(); } // assumes GL state is set for 2D void LLDragHandleLeft::draw() { /* Disable lines. Can drag anywhere in most windows. JC if( getVisible() && mEnabled && mForeground ) { const S32 BORDER_PAD = 2; // const S32 HPAD = 2; const S32 VPAD = 2; const S32 LINE_SPACING = 3; S32 left = BORDER_PAD + LINE_SPACING; S32 top = mRect.getHeight() - 2 * VPAD; // S32 right = mRect.getWidth() - HPAD; S32 bottom = VPAD; // draw lines for drag areas // no titles yet //LLRect title_rect = mTitleBox->getRect(); //S32 title_right = title_rect.mLeft + mTitleWidth; //BOOL show_right_side = title_right < mRect.getWidth(); S32 line = left; for( S32 i=0; i<4; i++ ) { gl_line_2d(line, top, line, bottom, mDragHighlightColor); gl_line_2d(line+1, top, line+1, bottom, mDragShadowColor); line += LINE_SPACING; } } */ // Colorize the text to match the frontmost state if (mTitleBox) { mTitleBox->setEnabled(mForeground); } LLView::draw(); } void LLDragHandleTop::reshapeTitleBox() { const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF ); S32 title_width = font->getWidth( mTitleBox->getText() ) + TITLE_PAD; if (mMaxTitleWidth > 0) title_width = llmin(title_width, mMaxTitleWidth); S32 title_height = llround(font->getLineHeight()); LLRect title_rect; title_rect.setLeftTopAndSize( LEFT_PAD, mRect.getHeight() - BORDER_PAD, mRect.getWidth() - LEFT_PAD - RIGHT_PAD, title_height); mTitleBox->setRect( title_rect ); } void LLDragHandleTop::reshape(S32 width, S32 height, BOOL called_from_parent) { LLView::reshape(width, height, called_from_parent); reshapeTitleBox(); } void LLDragHandleLeft::reshape(S32 width, S32 height, BOOL called_from_parent) { LLView::reshape(width, height, called_from_parent); } //------------------------------------------------------------- // UI event handling //------------------------------------------------------------- BOOL LLDragHandle::handleMouseDown(S32 x, S32 y, MASK mask) { // Route future Mouse messages here preemptively. (Release on mouse up.) // No handler needed for focus lost since this clas has no state that depends on it. gFocusMgr.setMouseCapture(this); localPointToScreen(x, y, &mDragLastScreenX, &mDragLastScreenY); mLastMouseScreenX = mDragLastScreenX; mLastMouseScreenY = mDragLastScreenY; // Note: don't pass on to children return TRUE; } BOOL LLDragHandle::handleMouseUp(S32 x, S32 y, MASK mask) { if( hasMouseCapture() ) { // Release the mouse gFocusMgr.setMouseCapture( NULL ); } // Note: don't pass on to children return TRUE; } BOOL LLDragHandle::handleHover(S32 x, S32 y, MASK mask) { BOOL handled = FALSE; // We only handle the click if the click both started and ended within us if( hasMouseCapture() ) { S32 screen_x; S32 screen_y; localPointToScreen(x, y, &screen_x, &screen_y); // Resize the parent S32 delta_x = screen_x - mDragLastScreenX; S32 delta_y = screen_y - mDragLastScreenY; LLRect original_rect = getParent()->getRect(); LLRect translated_rect = getParent()->getRect(); translated_rect.translate(delta_x, delta_y); // temporarily slam dragged window to new position getParent()->setRect(translated_rect); S32 pre_snap_x = getParent()->getRect().mLeft; S32 pre_snap_y = getParent()->getRect().mBottom; mDragLastScreenX = screen_x; mDragLastScreenY = screen_y; LLRect new_rect; LLCoordGL mouse_dir; // use hysteresis on mouse motion to preserve user intent when mouse stops moving mouse_dir.mX = (screen_x == mLastMouseScreenX) ? mLastMouseDir.mX : screen_x - mLastMouseScreenX; mouse_dir.mY = (screen_y == mLastMouseScreenY) ? mLastMouseDir.mY : screen_y - mLastMouseScreenY; mLastMouseDir = mouse_dir; mLastMouseScreenX = screen_x; mLastMouseScreenY = screen_y; LLView* snap_view = getParent()->findSnapRect(new_rect, mouse_dir, SNAP_PARENT_AND_SIBLINGS, sSnapMargin); getParent()->snappedTo(snap_view); delta_x = new_rect.mLeft - pre_snap_x; delta_y = new_rect.mBottom - pre_snap_y; translated_rect.translate(delta_x, delta_y); // restore original rect so delta are detected, then call user reshape method to handle snapped floaters, etc getParent()->setRect(original_rect); getParent()->userSetShape(translated_rect); mDragLastScreenX += delta_x; mDragLastScreenY += delta_y; getWindow()->setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (active)" <setCursor(UI_CURSOR_ARROW); lldebugst(LLERR_USER_INPUT) << "hover handled by " << getName() << " (inactive)" << llendl; handled = TRUE; } // Note: don't pass on to children return handled; } void LLDragHandle::setValue(const LLSD& value) { setTitle(value.asString()); }