00001
00032 #include "llviewerprecompiledheaders.h"
00033
00034
00035 #include "llhoverview.h"
00036
00037
00038 #include "llfontgl.h"
00039 #include "message.h"
00040 #include "llgl.h"
00041 #include "llfontgl.h"
00042 #include "llparcel.h"
00043 #include "lldbstrings.h"
00044 #include "llclickaction.h"
00045
00046
00047 #include "llagent.h"
00048 #include "llcachename.h"
00049 #include "llviewercontrol.h"
00050 #include "lldrawable.h"
00051 #include "llpermissions.h"
00052 #include "llresmgr.h"
00053 #include "llselectmgr.h"
00054 #include "lltoolmgr.h"
00055 #include "lltoolpie.h"
00056 #include "lltoolselectland.h"
00057 #include "llui.h"
00058 #include "llviewercamera.h"
00059 #include "llviewerobject.h"
00060 #include "llviewerobjectlist.h"
00061 #include "llviewerparcelmgr.h"
00062 #include "llviewerregion.h"
00063 #include "llviewerwindow.h"
00064 #include "llglheaders.h"
00065 #include "llviewerimagelist.h"
00066
00067 #include "llhudmanager.h"
00068
00069 #include "llhudmanager.h"
00070 #include "llhudeffect.h"
00071
00072
00073
00074
00075 const char* DEFAULT_DESC = "(No Description)";
00076 const F32 DELAY_BEFORE_SHOW_TIP = 0.35f;
00077
00078
00079
00080
00081
00082 LLHoverView *gHoverView = NULL;
00083
00084
00085
00086
00087 BOOL LLHoverView::sShowHoverTips = TRUE;
00088
00089
00090
00091
00092
00093 LLHoverView::LLHoverView(const std::string& name, const LLRect& rect)
00094 : LLView(name, rect, FALSE)
00095 {
00096 mDoneHoverPick = FALSE;
00097 mStartHoverPickTimer = FALSE;
00098 mHoverActive = FALSE;
00099 mUseHover = TRUE;
00100 mTyping = FALSE;
00101 mHoverOffset.clearVec();
00102 }
00103
00104 LLHoverView::~LLHoverView()
00105 {
00106
00107 mText.deleteAllData();
00108 }
00109
00110 EWidgetType LLHoverView::getWidgetType() const
00111 {
00112 return WIDGET_TYPE_HOVER_VIEW;
00113 }
00114
00115 LLString LLHoverView::getWidgetTag() const
00116 {
00117 return LL_HOVER_VIEW_TAG;
00118 }
00119
00120 void LLHoverView::updateHover(LLTool* current_tool)
00121 {
00122 BOOL picking_tool = ( current_tool == gToolPie
00123 || current_tool == gToolParcel );
00124
00125 mUseHover = !gAgent.cameraMouselook()
00126 && picking_tool
00127 && !mTyping;
00128 if (mUseHover)
00129 {
00130 if ((gViewerWindow->getMouseVelocityStat()->getPrev(0) < 0.01f)
00131 && (gCamera->getAngularVelocityStat()->getPrev(0) < 0.01f)
00132 && (gCamera->getVelocityStat()->getPrev(0) < 0.01f))
00133 {
00134 if (!mStartHoverPickTimer)
00135 {
00136 mStartHoverTimer.reset();
00137 mStartHoverPickTimer = TRUE;
00138
00139 mText.deleteAllData();
00140 }
00141
00142 if (mDoneHoverPick)
00143 {
00144
00145 updateText();
00146 }
00147 else if (mStartHoverTimer.getElapsedTimeF32() > DELAY_BEFORE_SHOW_TIP)
00148 {
00149 gViewerWindow->hitObjectOrLandGlobalAsync(gViewerWindow->getCurrentMouseX(),
00150 gViewerWindow->getCurrentMouseY(), 0, pickCallback );
00151 }
00152 }
00153 else
00154 {
00155 cancelHover();
00156 }
00157 }
00158
00159 }
00160
00161 void LLHoverView::pickCallback(S32 x, S32 y, MASK mask)
00162 {
00163 LLViewerObject* hit_obj = gViewerWindow->lastObjectHit();
00164
00165 if (hit_obj)
00166 {
00167 gHoverView->setHoverActive(TRUE);
00168 gSelectMgr->setHoverObject(hit_obj);
00169 gHoverView->mLastHoverObject = hit_obj;
00170 gHoverView->mHoverOffset = gViewerWindow->lastObjectHitOffset();
00171 }
00172 else
00173 {
00174 gHoverView->mLastHoverObject = NULL;
00175 }
00176
00177
00178 if (!hit_obj && gLastHitPosGlobal != LLVector3d::zero)
00179 {
00180 gHoverView->setHoverActive(TRUE);
00181 gHoverView->mHoverLandGlobal = gLastHitPosGlobal;
00182 gParcelMgr->requestHoverParcelProperties( gHoverView->mHoverLandGlobal );
00183 }
00184 else
00185 {
00186 gHoverView->mHoverLandGlobal = LLVector3d::zero;
00187 }
00188
00189 gHoverView->mDoneHoverPick = TRUE;
00190 }
00191
00192 void LLHoverView::setTyping(BOOL b)
00193 {
00194 mTyping = b;
00195 }
00196
00197
00198 void LLHoverView::cancelHover()
00199 {
00200 mStartHoverTimer.reset();
00201 mDoneHoverPick = FALSE;
00202 mStartHoverPickTimer = FALSE;
00203
00204 gSelectMgr->setHoverObject(NULL);
00205
00206
00207
00208
00209 setHoverActive(FALSE);
00210 }
00211
00212 void LLHoverView::resetLastHoverObject()
00213 {
00214 mLastHoverObject = NULL;
00215 }
00216
00217 void LLHoverView::updateText()
00218 {
00219 char first_name[DB_FIRST_NAME_BUF_SIZE];
00220 char last_name[DB_LAST_NAME_BUF_SIZE];
00221 char group_name[DB_GROUP_NAME_BUF_SIZE];
00222
00223 LLViewerObject* hit_object = getLastHoverObject();
00224
00225 mText.deleteAllData();
00226 if ( hit_object )
00227 {
00228 if ( hit_object->isHUDAttachment() )
00229 {
00230
00231
00232 return;
00233 }
00234
00235 if ( hit_object->isAttachment() )
00236 {
00237
00238 LLViewerObject* root_edit = hit_object->getRootEdit();
00239 if (!root_edit)
00240 {
00241
00242 return;
00243 }
00244 hit_object = (LLViewerObject*)root_edit->getParent();
00245 if (!hit_object)
00246 {
00247
00248 return;
00249 }
00250 }
00251
00252 if (hit_object->isAvatar())
00253 {
00254 LLString *line = new LLString("");
00255 LLNameValue* title = hit_object->getNVPair("Title");
00256 LLNameValue* firstname = hit_object->getNVPair("FirstName");
00257 LLNameValue* lastname = hit_object->getNVPair("LastName");
00258 if (firstname && lastname)
00259 {
00260 if (title)
00261 {
00262 line->append(title->getString());
00263 line->append(1, ' ');
00264 }
00265 line->append(firstname->getString());
00266 line->append(1, ' ');
00267 line->append(lastname->getString());
00268 }
00269 else
00270 {
00271 line->append("Person");
00272 }
00273 mText.addDataAtEnd(line);
00274 }
00275 else
00276 {
00277
00278
00279
00280
00281
00282
00283
00284 BOOL suppressObjectHoverDisplay = !gSavedSettings.getBOOL("ShowAllObjectHoverTip");
00285
00286 LLSelectNode *nodep = gSelectMgr->getHoverNode();;
00287 if (nodep)
00288 {
00289 char cstring[256];
00290 LLString *temp_str = NULL;
00291
00292 temp_str = new LLString();
00293 if (nodep->mName.empty())
00294 {
00295 temp_str->append("(no name)");
00296 }
00297 else
00298 {
00299 temp_str->append( nodep->mName );
00300 }
00301
00302 mText.addDataAtEnd(temp_str);
00303
00304 if (!nodep->mDescription.empty()
00305 && nodep->mDescription != DEFAULT_DESC)
00306 {
00307 temp_str = new LLString( nodep->mDescription );
00308 mText.addDataAtEnd( temp_str );
00309 }
00310
00311
00312 temp_str = new LLString();
00313 temp_str->append("Owner: ");
00314
00315 if (nodep->mValid)
00316 {
00317 LLUUID owner;
00318 if (!nodep->mPermissions->isGroupOwned())
00319 {
00320 owner = nodep->mPermissions->getOwner();
00321 if (LLUUID::null == owner)
00322 {
00323 temp_str->append("Public");
00324 }
00325 else if(gCacheName->getName(
00326 owner, first_name, last_name))
00327 {
00328 temp_str->append(first_name);
00329 temp_str->append(" ");
00330 temp_str->append(last_name);
00331 }
00332 else
00333 {
00334 temp_str->append("Retrieving...");
00335 }
00336 }else
00337 {
00338 owner = nodep->mPermissions->getGroup();
00339 if (gCacheName->getGroupName(owner, group_name))
00340 {
00341 temp_str->append(group_name);
00342 temp_str->append("(Group)");
00343 }
00344 else
00345 {
00346 temp_str->append("Retrieving...");
00347 }
00348 }
00349 }
00350 else
00351 {
00352 temp_str->append("Retrieving...");
00353 }
00354 mText.addDataAtEnd(temp_str);
00355
00356
00357
00358 LLViewerObject *object = hit_object;
00359 LLViewerObject *parent = (LLViewerObject *)object->getParent();
00360
00361 if (object &&
00362 (object->usePhysics() ||
00363 object->flagScripted() ||
00364 object->flagHandleTouch() || (parent && parent->flagHandleTouch()) ||
00365 object->flagTakesMoney() || (parent && parent->flagTakesMoney()) ||
00366 object->flagAllowInventoryAdd() ||
00367 object->flagTemporary() ||
00368 object->flagPhantom()) )
00369 {
00370 temp_str = new LLString();
00371 if (object->flagScripted())
00372 {
00373 temp_str->append("Script ");
00374 }
00375
00376 if (object->usePhysics())
00377 {
00378 temp_str->append("Physics ");
00379 }
00380
00381 if (object->flagHandleTouch() || (parent && parent->flagHandleTouch()) )
00382 {
00383 temp_str->append("Touch ");
00384 suppressObjectHoverDisplay = FALSE;
00385 }
00386
00387 if (object->flagTakesMoney() || (parent && parent->flagTakesMoney()) )
00388 {
00389 temp_str->append("L$ ");
00390 suppressObjectHoverDisplay = FALSE;
00391 }
00392
00393 if (object->flagAllowInventoryAdd())
00394 {
00395 temp_str->append("Drop Inventory ");
00396 suppressObjectHoverDisplay = FALSE;
00397 }
00398
00399 if (object->flagPhantom())
00400 {
00401 temp_str->append("Phantom ");
00402 }
00403
00404 if (object->flagTemporary())
00405 {
00406 temp_str->append("Temporary ");
00407 }
00408
00409 if (object->usePhysics() ||
00410 object->flagHandleTouch() ||
00411 (parent && parent->flagHandleTouch()) )
00412 {
00413 temp_str->append("(Right-click for menu) ");
00414 }
00415 mText.addDataAtEnd(temp_str);
00416 }
00417
00418 if (nodep->mValid)
00419 {
00420 BOOL for_copy = nodep->mPermissions->getMaskEveryone() & PERM_COPY && object->permCopy();
00421 BOOL for_sale = nodep->mSaleInfo.isForSale() &&
00422 nodep->mPermissions->getMaskOwner() & PERM_TRANSFER &&
00423 (nodep->mPermissions->getMaskOwner() & PERM_COPY ||
00424 nodep->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY);
00425 if (for_copy)
00426 {
00427 temp_str = new LLString();
00428 temp_str->append("Free to copy");
00429 mText.addDataAtEnd(temp_str);
00430 suppressObjectHoverDisplay = FALSE;
00431 }
00432 else if (for_sale)
00433 {
00434 temp_str = new LLString();
00435 temp_str->append("For Sale: ");
00436 snprintf(cstring, sizeof(cstring), "L$%d", nodep->mSaleInfo.getSalePrice());
00437 temp_str->append(cstring);
00438 mText.addDataAtEnd(temp_str);
00439 suppressObjectHoverDisplay = FALSE;
00440 }
00441 else
00442 {
00443
00444
00445
00446 }
00447 }
00448 else
00449 {
00450 temp_str = new LLString();
00451 temp_str->append("For Sale: Retrieving...");
00452 mText.addDataAtEnd(temp_str);
00453 }
00454 }
00455
00456 if (suppressObjectHoverDisplay)
00457 {
00458 mText.deleteAllData();
00459 }
00460 }
00461 }
00462 else if ( mHoverLandGlobal != LLVector3d::zero )
00463 {
00464
00465
00466
00467
00468
00469 if (!gSavedSettings.getBOOL("ShowLandHoverTip")) return;
00470
00471
00472
00473 LLString *line = NULL;
00474
00475 LLParcel* hover_parcel = gParcelMgr->getHoverParcel();
00476 LLUUID owner;
00477 S32 width = 0;
00478 S32 height = 0;
00479
00480 if ( hover_parcel )
00481 {
00482 owner = hover_parcel->getOwnerID();
00483 width = S32(gParcelMgr->getHoverParcelWidth());
00484 height = S32(gParcelMgr->getHoverParcelHeight());
00485 }
00486
00487
00488 line = new LLString();
00489 mText.addDataAtEnd(line);
00490
00491 line->append("Land: ");
00492 if (hover_parcel)
00493 {
00494 line->append(hover_parcel->getName());
00495 }
00496
00497
00498 line = new LLString();
00499 mText.addDataAtEnd(line);
00500
00501 line->append("Owner: ");
00502
00503 if ( hover_parcel )
00504 {
00505 if (LLUUID::null == owner)
00506 {
00507 line->append("Public");
00508 }
00509 else if (hover_parcel->getIsGroupOwned())
00510 {
00511 if (gCacheName->getGroupName(owner, group_name))
00512 {
00513 line->append(group_name);
00514 line->append("(Group)");
00515 }
00516 else
00517 {
00518 line->append("Retrieving...");
00519 }
00520 }
00521 else if(gCacheName->getName(owner, first_name, last_name))
00522 {
00523 line->append(first_name);
00524 line->append(" ");
00525 line->append(last_name);
00526 }
00527 else
00528 {
00529 line->append("Retrieving...");
00530 }
00531 }
00532 else
00533 {
00534 line->append("Retrieving...");
00535 }
00536
00537
00538
00539
00540
00541 if ( hover_parcel && owner != gAgent.getID() )
00542 {
00543 S32 words = 0;
00544 line = new LLString("");
00545
00546
00547
00548 if ( !hover_parcel->getAllowModify() )
00549 {
00550 if ( hover_parcel->getAllowGroupModify() )
00551 {
00552 line->append("Group Build");
00553 }
00554 else
00555 {
00556 line->append("No Build");
00557 }
00558
00559 words++;
00560 }
00561
00562 if ( !hover_parcel->getAllowTerraform() )
00563 {
00564 if (words) line->append(", ");
00565 line->append("No Edit");
00566 words++;
00567 }
00568
00569 if ( hover_parcel->getAllowDamage() )
00570 {
00571 if (words) line->append(", ");
00572 line->append("Not Safe");
00573 words++;
00574 }
00575
00576
00577 if ( !hover_parcel->getAllowFly() )
00578 {
00579 if (words) line->append(", ");
00580 line->append("No Fly");
00581 words++;
00582 }
00583
00584 if ( !hover_parcel->getAllowOtherScripts() )
00585 {
00586 if (words) line->append(", ");
00587 if ( hover_parcel->getAllowGroupScripts() )
00588 {
00589 line->append("Group Scripts");
00590 }
00591 else
00592 {
00593 line->append("No Scripts");
00594 }
00595
00596 words++;
00597 }
00598
00599 if (words)
00600 {
00601 mText.addDataAtEnd(line);
00602 }
00603 else
00604 {
00605 delete line;
00606 line = NULL;
00607 }
00608 }
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623 if (hover_parcel && hover_parcel->getParcelFlag(PF_FOR_SALE))
00624 {
00625 char buffer[MAX_STRING];
00626 snprintf(buffer, sizeof(buffer), "For Sale: L$%d", hover_parcel->getSalePrice() );
00627
00628 line = new LLString(buffer);
00629 mText.addDataAtEnd(line);
00630 }
00631 }
00632 }
00633
00634
00635 void LLHoverView::draw()
00636 {
00637 if( !getVisible() )
00638 {
00639 return;
00640 }
00641
00642 if ( !isHovering() )
00643 {
00644 return;
00645 }
00646
00647
00648
00649
00650 if (!sShowHoverTips) return;
00651
00652 const F32 MAX_HOVER_DISPLAY_SECS = 5.f;
00653 if (mHoverTimer.getElapsedTimeF32() > MAX_HOVER_DISPLAY_SECS)
00654 {
00655 return;
00656 }
00657
00658 const F32 MAX_ALPHA = 0.9f;
00659
00660 F32 alpha;
00661 if (mHoverActive)
00662 {
00663 alpha = 1.f;
00664
00665 if (isHoveringObject())
00666 {
00667
00668 LLViewerObject *hover_object = getLastHoverObject();
00669 if (hover_object->isAvatar())
00670 {
00671 gAgent.setLookAt(LOOKAT_TARGET_HOVER, getLastHoverObject(), LLVector3::zero);
00672 }
00673 else
00674 {
00675 LLVector3 local_offset((F32)mHoverOffset.mdV[VX], (F32)mHoverOffset.mdV[VY], (F32)mHoverOffset.mdV[VZ]);
00676 gAgent.setLookAt(LOOKAT_TARGET_HOVER, getLastHoverObject(), local_offset);
00677 }
00678 }
00679 }
00680 else
00681 {
00682 alpha = llmax(0.f, MAX_ALPHA - mHoverTimer.getElapsedTimeF32()*2.f);
00683 }
00684
00685
00686 if (mText.isEmpty())
00687 {
00688 return;
00689 }
00690
00691
00692 if (alpha <= 0.f)
00693 {
00694 return;
00695 }
00696
00697 LLViewerImage* box_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE);
00698 LLViewerImage* shadow_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square_soft.tga")), MIPMAP_FALSE, TRUE);
00699
00700 const LLFontGL* fontp = gResMgr->getRes(LLFONT_SANSSERIF_SMALL);
00701
00702
00703 LLColor4 text_color = gColors.getColor("ToolTipTextColor");
00704
00705 LLColor4 bg_color = gColors.getColor("ToolTipBgColor");
00706 LLColor4 shadow_color = gColors.getColor("ColorDropShadow");
00707
00708
00709
00710
00711
00712
00713 S32 max_width = 0;
00714 S32 num_lines = mText.getLength();
00715 LLString *cur_stringp;
00716 for (cur_stringp = mText.getFirstData(); cur_stringp; cur_stringp = mText.getNextData())
00717 {
00718 max_width = llmax(max_width, (S32)fontp->getWidth(*cur_stringp));
00719 }
00720
00721 S32 left = mHoverPos.mX + 10;
00722 S32 top = mHoverPos.mY - 16;
00723 S32 right = mHoverPos.mX + max_width + 30;
00724 S32 bottom = mHoverPos.mY - 24 - llfloor(num_lines*fontp->getLineHeight());
00725
00726
00727 if (mHoverActive
00728 && isHoveringObject()
00729 && mLastHoverObject->getClickAction() != CLICK_ACTION_NONE)
00730 {
00731 const S32 CLICK_OFFSET = 10;
00732 top -= CLICK_OFFSET;
00733 bottom -= CLICK_OFFSET;
00734 }
00735
00736
00737 LLRect old_rect = mRect;
00738 mRect.set( left, top, right, bottom );
00739 translateIntoRect( gViewerWindow->getVirtualWindowRect(), FALSE );
00740 left = mRect.mLeft;
00741 top = mRect.mTop;
00742 right = mRect.mRight;
00743 bottom = mRect.mBottom;
00744 mRect = old_rect;
00745
00746 LLGLSUIDefault gls_ui;
00747
00748 shadow_color.mV[VALPHA] = 0.7f * alpha;
00749 S32 shadow_offset = gSavedSettings.getS32("DropShadowTooltip");
00750 glColor4fv(shadow_color.mV);
00751 LLViewerImage::bindTexture(shadow_imagep);
00752 gl_segmented_rect_2d_tex(left + shadow_offset, top - shadow_offset, right + shadow_offset, bottom - shadow_offset, shadow_imagep->getWidth(), shadow_imagep->getHeight(), 16);
00753
00754 bg_color.mV[VALPHA] = alpha;
00755 glColor4fv(bg_color.mV);
00756 LLViewerImage::bindTexture(box_imagep);
00757 gl_segmented_rect_2d_tex(left, top, right, bottom, box_imagep->getWidth(), box_imagep->getHeight(), 16);
00758
00759 S32 cur_offset = top - 4;
00760 for (cur_stringp = mText.getFirstData(); cur_stringp; cur_stringp = mText.getNextData())
00761 {
00762 fontp->renderUTF8(*cur_stringp, 0, left + 10, cur_offset, text_color, LLFontGL::LEFT, LLFontGL::TOP);
00763 cur_offset -= llfloor(fontp->getLineHeight());
00764 }
00765 }
00766
00767 void LLHoverView::setHoverActive(const BOOL active)
00768 {
00769 if (active != mHoverActive)
00770 {
00771 mHoverTimer.reset();
00772 }
00773
00774 mHoverActive = active;
00775
00776 if (active)
00777 {
00778 mHoverPos = gViewerWindow->getCurrentMouse();
00779 }
00780 }
00781
00782
00783 BOOL LLHoverView::isHoveringLand() const
00784 {
00785 return !mHoverLandGlobal.isExactlyZero();
00786 }
00787
00788
00789 BOOL LLHoverView::isHoveringObject() const
00790 {
00791 return !mLastHoverObject.isNull() && !mLastHoverObject->isDead();
00792 }
00793
00794
00795 LLViewerObject* LLHoverView::getLastHoverObject() const
00796 {
00797 if (!mLastHoverObject.isNull() && !mLastHoverObject->isDead())
00798 {
00799 return mLastHoverObject;
00800 }
00801 else
00802 {
00803 return NULL;
00804 }
00805 }
00806
00807