/** * @file llconversationview.cpp * @brief Implementation of conversations list widgets and views * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #include "llviewerprecompiledheaders.h" #include "llconversationview.h" #include "llconversationmodel.h" #include "llimconversation.h" #include "llimfloatercontainer.h" // // Implementation of conversations list session widgets // static LLDefaultChildRegistry::Register r_conversation_view_session("conversation_view_session"); LLConversationViewSession::Params::Params() : container() {} LLConversationViewSession::LLConversationViewSession(const LLConversationViewSession::Params& p): LLFolderViewFolder(p), mContainer(p.container), mItemPanel(NULL), mSessionTitle(NULL) { } BOOL LLConversationViewSession::postBuild() { LLFolderViewItem::postBuild(); mItemPanel = LLUICtrlFactory::getInstance()->createFromFile("panel_conversation_list_item.xml", NULL, LLPanel::child_registry_t::instance()); addChild(mItemPanel); mSessionTitle = mItemPanel->getChild("conversation_title"); refresh(); return TRUE; } void LLConversationViewSession::draw() { // *TODO Seth PE: remove the code duplicated from LLFolderViewFolder::draw() // ***** LLFolderViewFolder::draw() code begin ***** if (mAutoOpenCountdown != 0.f) { mControlLabelRotation = mAutoOpenCountdown * -90.f; } else if (isOpen()) { mControlLabelRotation = lerp(mControlLabelRotation, -90.f, LLCriticalDamp::getInterpolant(0.04f)); } else { mControlLabelRotation = lerp(mControlLabelRotation, 0.f, LLCriticalDamp::getInterpolant(0.025f)); } // ***** LLFolderViewFolder::draw() code end ***** // *TODO Seth PE: remove the code duplicated from LLFolderViewItem::draw() // ***** LLFolderViewItem::draw() code begin ***** const LLColor4U DEFAULT_WHITE(255, 255, 255); static LLUIColor sFgColor = LLUIColorTable::instance().getColor("MenuItemEnabledColor", DEFAULT_WHITE); static LLUIColor sHighlightBgColor = LLUIColorTable::instance().getColor("MenuItemHighlightBgColor", DEFAULT_WHITE); static LLUIColor sFocusOutlineColor = LLUIColorTable::instance().getColor("InventoryFocusOutlineColor", DEFAULT_WHITE); static LLUIColor sMouseOverColor = LLUIColorTable::instance().getColor("InventoryMouseOverColor", DEFAULT_WHITE); const LLFolderViewItem::Params& default_params = LLUICtrlFactory::getDefaultParams(); const S32 TOP_PAD = default_params.item_top_pad; const S32 FOCUS_LEFT = 1; getViewModelItem()->update(); //--------------------------------------------------------------------------------// // Draw open folder arrow // if (hasVisibleChildren() || getViewModelItem()->hasChildren()) { LLUIImage* arrow_image = default_params.folder_arrow_image; gl_draw_scaled_rotated_image( mIndentation, getRect().getHeight() - ARROW_SIZE - TEXT_PAD - TOP_PAD, ARROW_SIZE, ARROW_SIZE, mControlLabelRotation, arrow_image->getImage(), sFgColor); } //--------------------------------------------------------------------------------// // Draw highlight for selected items // const BOOL show_context = (getRoot() ? getRoot()->getShowSelectionContext() : FALSE); const BOOL filled = show_context || (getRoot() ? getRoot()->getParentPanel()->hasFocus() : FALSE); // If we have keyboard focus, draw selection filled const S32 focus_top = getRect().getHeight(); const S32 focus_bottom = getRect().getHeight() - mItemHeight; const bool folder_open = (getRect().getHeight() > mItemHeight + 4); if (mIsSelected) // always render "current" item. Only render other selected items if mShowSingleSelection is FALSE { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); LLColor4 bg_color = sHighlightBgColor; if (!mIsCurSelection) { // do time-based fade of extra objects F32 fade_time = (getRoot() ? getRoot()->getSelectionFadeElapsedTime() : 0.0f); if (getRoot() && getRoot()->getShowSingleSelection()) { // fading out bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, bg_color.mV[VALPHA], 0.f); } else { // fading in bg_color.mV[VALPHA] = clamp_rescale(fade_time, 0.f, 0.4f, 0.f, bg_color.mV[VALPHA]); } } gl_rect_2d(FOCUS_LEFT, focus_top, getRect().getWidth() - 2, focus_bottom, bg_color, filled); if (mIsCurSelection) { gl_rect_2d(FOCUS_LEFT, focus_top, getRect().getWidth() - 2, focus_bottom, sFocusOutlineColor, FALSE); } if (folder_open) { gl_rect_2d(FOCUS_LEFT, focus_bottom + 1, // overlap with bottom edge of above rect getRect().getWidth() - 2, 0, sFocusOutlineColor, FALSE); if (show_context) { gl_rect_2d(FOCUS_LEFT, focus_bottom + 1, getRect().getWidth() - 2, 0, sHighlightBgColor, TRUE); } } } else if (mIsMouseOverTitle) { gl_rect_2d(FOCUS_LEFT, focus_top, getRect().getWidth() - 2, focus_bottom, sMouseOverColor, FALSE); } //--------------------------------------------------------------------------------// // Draw DragNDrop highlight // if (mDragAndDropTarget) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gl_rect_2d(FOCUS_LEFT, focus_top, getRect().getWidth() - 2, focus_bottom, sHighlightBgColor, FALSE); if (folder_open) { gl_rect_2d(FOCUS_LEFT, focus_bottom + 1, // overlap with bottom edge of above rect getRect().getWidth() - 2, 0, sHighlightBgColor, FALSE); } mDragAndDropTarget = FALSE; } // ***** LLFolderViewItem::draw() code end ***** // draw children if root folder, or any other folder that is open or animating to closed state bool draw_children = getRoot() == static_cast(this) || isOpen() || mCurHeight != mTargetHeight; for (folders_t::iterator iter = mFolders.begin(); iter != mFolders.end();) { folders_t::iterator fit = iter++; (*fit)->setVisible(draw_children); } for (items_t::iterator iter = mItems.begin(); iter != mItems.end();) { items_t::iterator iit = iter++; (*iit)->setVisible(draw_children); } LLView::draw(); } // virtual S32 LLConversationViewSession::arrange(S32* width, S32* height) { LLRect rect(getIndentation() + ARROW_SIZE, getLocalRect().mTop, getLocalRect().mRight, getLocalRect().mTop - getItemHeight()); mItemPanel->setShape(rect); return LLFolderViewFolder::arrange(width, height); } void LLConversationViewSession::selectItem() { LLFolderViewItem::selectItem(); LLConversationItem* item = dynamic_cast(mViewModelItem); LLFloater* session_floater = LLIMConversation::getConversation(item->getUUID()); LLMultiFloater* host_floater = session_floater->getHost(); if (host_floater == mContainer) { // Always expand the message pane if the panel is hosted by the container mContainer->collapseMessagesPane(false); // Switch to the conversation floater that is being selected mContainer->selectFloater(session_floater); } // Set the focus on the selected floater session_floater->setFocus(TRUE); } void LLConversationViewSession::setVisibleIfDetached(BOOL visible) { // Do this only if the conversation floater has been torn off (i.e. no multi floater host) and is not minimized // Note: minimized dockable floaters are brought to front hence unminimized when made visible and we don't want that here LLConversationItem* item = dynamic_cast(mViewModelItem); LLFloater* session_floater = LLIMConversation::getConversation(item->getUUID()); if (session_floater && !session_floater->getHost() && !session_floater->isMinimized()) { session_floater->setVisible(visible); } } LLConversationViewParticipant* LLConversationViewSession::findParticipant(const LLUUID& participant_id) { // This is *not* a general tree parsing algorithm. We search only in the mItems list // assuming there is no mFolders which makes sense for sessions (sessions don't contain // sessions). LLConversationViewParticipant* participant = NULL; items_t::const_iterator iter; for (iter = getItemsBegin(); iter != getItemsEnd(); iter++) { participant = dynamic_cast(*iter); if (participant->hasSameValue(participant_id)) { break; } } return (iter == getItemsEnd() ? NULL : participant); } void LLConversationViewSession::refresh() { // Refresh the session view from its model data LLConversationItem* vmi = dynamic_cast(getViewModelItem()); vmi->resetRefresh(); if (mSessionTitle) { mSessionTitle->setText(vmi->getDisplayName()); } // Note: for the moment, all that needs to be done is done by LLFolderViewItem::refresh() // Do the regular upstream refresh LLFolderViewFolder::refresh(); } // // Implementation of conversations list participant (avatar) widgets // LLConversationViewParticipant::Params::Params() : participant_id() {} LLConversationViewParticipant::LLConversationViewParticipant( const LLConversationViewParticipant::Params& p ): LLFolderViewItem(p), mUUID(p.participant_id) { } void LLConversationViewParticipant::refresh() { // Refresh the participant view from its model data LLConversationItem* vmi = dynamic_cast(getViewModelItem()); vmi->resetRefresh(); // Note: for the moment, all that needs to be done is done by LLFolderViewItem::refresh() // Do the regular upstream refresh LLFolderViewItem::refresh(); } // EOF