diff options
Diffstat (limited to 'linden/indra/newview/llhoverview.cpp')
-rw-r--r-- | linden/indra/newview/llhoverview.cpp | 801 |
1 files changed, 801 insertions, 0 deletions
diff --git a/linden/indra/newview/llhoverview.cpp b/linden/indra/newview/llhoverview.cpp new file mode 100644 index 0000000..9c403f4 --- /dev/null +++ b/linden/indra/newview/llhoverview.cpp | |||
@@ -0,0 +1,801 @@ | |||
1 | /** | ||
2 | * @file llhoverview.cpp | ||
3 | * @brief LLHoverView class implementation | ||
4 | * | ||
5 | * Copyright (c) 2001-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "llviewerprecompiledheaders.h" | ||
29 | |||
30 | // self include | ||
31 | #include "llhoverview.h" | ||
32 | |||
33 | // Library includes | ||
34 | #include "llfontgl.h" | ||
35 | #include "message.h" | ||
36 | #include "llgl.h" | ||
37 | #include "llfontgl.h" | ||
38 | #include "llparcel.h" | ||
39 | #include "lldbstrings.h" | ||
40 | #include "llclickaction.h" | ||
41 | |||
42 | // Viewer includes | ||
43 | #include "llagent.h" | ||
44 | #include "llcachename.h" | ||
45 | #include "llviewercontrol.h" | ||
46 | #include "lldrawable.h" | ||
47 | #include "llpermissions.h" | ||
48 | #include "llresmgr.h" | ||
49 | #include "llselectmgr.h" | ||
50 | #include "lltoolmgr.h" | ||
51 | #include "lltoolpie.h" | ||
52 | #include "lltoolselectland.h" | ||
53 | #include "llui.h" | ||
54 | #include "llviewercamera.h" | ||
55 | #include "llviewerobject.h" | ||
56 | #include "llviewerobjectlist.h" | ||
57 | #include "llviewerparcelmgr.h" | ||
58 | #include "llviewerregion.h" | ||
59 | #include "llviewerwindow.h" | ||
60 | #include "llglheaders.h" | ||
61 | #include "llviewerimagelist.h" | ||
62 | //#include "lltoolobjpicker.h" | ||
63 | #include "llhudmanager.h" // HACK for creating flex obj's | ||
64 | |||
65 | #include "llhudmanager.h" // For testing effects | ||
66 | #include "llhudeffect.h" | ||
67 | |||
68 | // | ||
69 | // Constants | ||
70 | // | ||
71 | const char* DEFAULT_DESC = "(No Description)"; | ||
72 | const F32 DELAY_BEFORE_SHOW_TIP = 0.35f; | ||
73 | |||
74 | // | ||
75 | // Local globals | ||
76 | // | ||
77 | |||
78 | LLHoverView *gHoverView = NULL; | ||
79 | |||
80 | // | ||
81 | // Static member functions | ||
82 | // | ||
83 | BOOL LLHoverView::sShowHoverTips = TRUE; | ||
84 | |||
85 | // | ||
86 | // Member functions | ||
87 | // | ||
88 | |||
89 | LLHoverView::LLHoverView(const std::string& name, const LLRect& rect) | ||
90 | : LLView(name, rect, FALSE) | ||
91 | { | ||
92 | mDoneHoverPick = FALSE; | ||
93 | mStartHoverPickTimer = FALSE; | ||
94 | mHoverActive = FALSE; | ||
95 | mUseHover = TRUE; | ||
96 | mTyping = FALSE; | ||
97 | mHoverOffset.clearVec(); | ||
98 | } | ||
99 | |||
100 | LLHoverView::~LLHoverView() | ||
101 | { | ||
102 | // children all deleted by LLView destructor | ||
103 | mText.deleteAllData(); | ||
104 | mConnectors.reset(); | ||
105 | } | ||
106 | |||
107 | EWidgetType LLHoverView::getWidgetType() const | ||
108 | { | ||
109 | return WIDGET_TYPE_HOVER_VIEW; | ||
110 | } | ||
111 | |||
112 | LLString LLHoverView::getWidgetTag() const | ||
113 | { | ||
114 | return LL_HOVER_VIEW_TAG; | ||
115 | } | ||
116 | |||
117 | void LLHoverView::updateHover(LLTool* current_tool) | ||
118 | { | ||
119 | BOOL picking_tool = ( current_tool == gToolPie | ||
120 | || current_tool == gToolParcel ); | ||
121 | |||
122 | mUseHover = !gAgent.cameraMouselook() | ||
123 | && picking_tool | ||
124 | && !mTyping; | ||
125 | if (mUseHover) | ||
126 | { | ||
127 | if ((gViewerWindow->getMouseVelocityStat()->getPrev(0) < 0.01f) | ||
128 | && (gCamera->getAngularVelocityStat()->getPrev(0) < 0.01f) | ||
129 | && (gCamera->getVelocityStat()->getPrev(0) < 0.01f)) | ||
130 | { | ||
131 | if (!mStartHoverPickTimer) | ||
132 | { | ||
133 | mStartHoverTimer.reset(); | ||
134 | mStartHoverPickTimer = TRUE; | ||
135 | // Delete the existing text so that we do not briefly show the wrong data. | ||
136 | mText.deleteAllData(); | ||
137 | } | ||
138 | |||
139 | if (mDoneHoverPick) | ||
140 | { | ||
141 | // Just update the hover data | ||
142 | updateText(); | ||
143 | } | ||
144 | else if (mStartHoverTimer.getElapsedTimeF32() > DELAY_BEFORE_SHOW_TIP) | ||
145 | { | ||
146 | gViewerWindow->hitObjectOrLandGlobalAsync(gViewerWindow->getCurrentMouseX(), | ||
147 | gViewerWindow->getCurrentMouseY(), 0, pickCallback, TRUE ); | ||
148 | } | ||
149 | } | ||
150 | else | ||
151 | { | ||
152 | cancelHover(); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | } | ||
157 | |||
158 | void LLHoverView::pickCallback(S32 x, S32 y, MASK mask) | ||
159 | { | ||
160 | LLViewerObject* hit_obj = gViewerWindow->lastObjectHit(); | ||
161 | |||
162 | if (hit_obj) | ||
163 | { | ||
164 | gHoverView->setHoverActive(TRUE); | ||
165 | gSelectMgr->setHoverObject(hit_obj); | ||
166 | gHoverView->mLastHoverObject = hit_obj; | ||
167 | gHoverView->mHoverOffset = gViewerWindow->lastObjectHitOffset(); | ||
168 | } | ||
169 | else | ||
170 | { | ||
171 | gHoverView->mLastHoverObject = NULL; | ||
172 | } | ||
173 | |||
174 | // We didn't hit an object, but we did hit land. | ||
175 | if (!hit_obj && gLastHitPosGlobal != LLVector3d::zero) | ||
176 | { | ||
177 | gHoverView->setHoverActive(TRUE); | ||
178 | gHoverView->mHoverLandGlobal = gLastHitPosGlobal; | ||
179 | gParcelMgr->requestHoverParcelProperties( gHoverView->mHoverLandGlobal ); | ||
180 | } | ||
181 | else | ||
182 | { | ||
183 | gHoverView->mHoverLandGlobal = LLVector3d::zero; | ||
184 | } | ||
185 | |||
186 | gHoverView->mDoneHoverPick = TRUE; | ||
187 | } | ||
188 | |||
189 | void LLHoverView::setTyping(BOOL b) | ||
190 | { | ||
191 | mTyping = b; | ||
192 | } | ||
193 | |||
194 | |||
195 | void LLHoverView::cancelHover() | ||
196 | { | ||
197 | mStartHoverTimer.reset(); | ||
198 | mDoneHoverPick = FALSE; | ||
199 | mStartHoverPickTimer = FALSE; | ||
200 | |||
201 | gSelectMgr->setHoverObject(NULL); | ||
202 | // Can't do this, some code relies on hover object still being | ||
203 | // set after the hover is cancelled! Dammit. JC | ||
204 | // mLastHoverObject = NULL; | ||
205 | |||
206 | setHoverActive(FALSE); | ||
207 | mConnectors.reset(); | ||
208 | } | ||
209 | |||
210 | void LLHoverView::resetLastHoverObject() | ||
211 | { | ||
212 | mLastHoverObject = NULL; | ||
213 | } | ||
214 | |||
215 | void LLHoverView::updateText() | ||
216 | { | ||
217 | char first_name[DB_FIRST_NAME_BUF_SIZE]; | ||
218 | char last_name[DB_LAST_NAME_BUF_SIZE]; | ||
219 | char group_name[DB_GROUP_NAME_BUF_SIZE]; | ||
220 | |||
221 | LLViewerObject* hit_object = getLastHoverObject(); | ||
222 | |||
223 | mText.deleteAllData(); | ||
224 | if ( hit_object ) | ||
225 | { | ||
226 | if ( hit_object->isAttachment() ) | ||
227 | { | ||
228 | // get root of attachment then parent, which is avatar | ||
229 | LLViewerObject* root_edit = hit_object->getRootEdit(); | ||
230 | if (!root_edit) | ||
231 | { | ||
232 | // Strange parenting issue, don't show any text | ||
233 | return; | ||
234 | } | ||
235 | hit_object = (LLViewerObject*)root_edit->getParent(); | ||
236 | if (!hit_object) | ||
237 | { | ||
238 | // another strange parenting issue, bail out | ||
239 | return; | ||
240 | } | ||
241 | } | ||
242 | |||
243 | if (hit_object->isAvatar()) | ||
244 | { | ||
245 | LLString *line = new LLString(""); | ||
246 | LLNameValue* title = hit_object->getNVPair("Title"); | ||
247 | LLNameValue* firstname = hit_object->getNVPair("FirstName"); | ||
248 | LLNameValue* lastname = hit_object->getNVPair("LastName"); | ||
249 | if (firstname && lastname) | ||
250 | { | ||
251 | if (title) | ||
252 | { | ||
253 | line->append(title->getString()); | ||
254 | line->append(1, ' '); | ||
255 | } | ||
256 | line->append(firstname->getString()); | ||
257 | line->append(1, ' '); | ||
258 | line->append(lastname->getString()); | ||
259 | } | ||
260 | else | ||
261 | { | ||
262 | line->append("Person"); | ||
263 | } | ||
264 | mText.addDataAtEnd(line); | ||
265 | } | ||
266 | else | ||
267 | { | ||
268 | // | ||
269 | // We have hit a regular object (not an avatar or attachment) | ||
270 | // | ||
271 | |||
272 | // | ||
273 | // Default prefs will suppress display unless the object is interactive | ||
274 | // | ||
275 | BOOL suppressObjectHoverDisplay = !gSavedSettings.getBOOL("ShowAllObjectHoverTip"); | ||
276 | |||
277 | |||
278 | LLSelectNodeList &nodes = gSelectMgr->getHoverObjects(); | ||
279 | LLSelectNode *nodep = nodes.getFirstRootNode(); | ||
280 | if (nodep) | ||
281 | { | ||
282 | char cstring[256]; | ||
283 | LLString *temp_str = NULL; | ||
284 | |||
285 | temp_str = new LLString(); | ||
286 | if (nodep->mName.empty()) | ||
287 | { | ||
288 | temp_str->append("(no name)"); | ||
289 | } | ||
290 | else | ||
291 | { | ||
292 | temp_str->append( nodep->mName ); | ||
293 | } | ||
294 | |||
295 | mText.addDataAtEnd(temp_str); | ||
296 | |||
297 | if (!nodep->mDescription.empty() | ||
298 | && nodep->mDescription != DEFAULT_DESC) | ||
299 | { | ||
300 | temp_str = new LLString( nodep->mDescription ); | ||
301 | mText.addDataAtEnd( temp_str ); | ||
302 | } | ||
303 | |||
304 | // Line: "Owner: James Linden" | ||
305 | temp_str = new LLString(); | ||
306 | temp_str->append("Owner: "); | ||
307 | |||
308 | if (nodep->mValid) | ||
309 | { | ||
310 | LLUUID owner; | ||
311 | if (!nodep->mPermissions->isGroupOwned()) | ||
312 | { | ||
313 | owner = nodep->mPermissions->getOwner(); | ||
314 | if (LLUUID::null == owner) | ||
315 | { | ||
316 | temp_str->append("Public"); | ||
317 | } | ||
318 | else if(gCacheName->getName( | ||
319 | owner, first_name, last_name)) | ||
320 | { | ||
321 | temp_str->append(first_name); | ||
322 | temp_str->append(" "); | ||
323 | temp_str->append(last_name); | ||
324 | } | ||
325 | else | ||
326 | { | ||
327 | temp_str->append("Retrieving..."); | ||
328 | } | ||
329 | }else | ||
330 | { | ||
331 | owner = nodep->mPermissions->getGroup(); | ||
332 | if (gCacheName->getGroupName(owner, group_name)) | ||
333 | { | ||
334 | temp_str->append(group_name); | ||
335 | temp_str->append("(Group)"); | ||
336 | } | ||
337 | else | ||
338 | { | ||
339 | temp_str->append("Retrieving..."); | ||
340 | } | ||
341 | } | ||
342 | } | ||
343 | else | ||
344 | { | ||
345 | temp_str->append("Retrieving..."); | ||
346 | } | ||
347 | mText.addDataAtEnd(temp_str); | ||
348 | |||
349 | // Build a line describing any special properties | ||
350 | // of this object. | ||
351 | LLViewerObject *object = hit_object; | ||
352 | LLViewerObject *parent = (LLViewerObject *)object->getParent(); | ||
353 | |||
354 | if (object && | ||
355 | (object->usePhysics() || | ||
356 | object->flagScripted() || | ||
357 | object->flagHandleTouch() || (parent && parent->flagHandleTouch()) || | ||
358 | object->flagTakesMoney() || (parent && parent->flagTakesMoney()) || | ||
359 | object->flagAllowInventoryAdd() || | ||
360 | object->flagTemporary() || | ||
361 | object->flagPhantom()) ) | ||
362 | { | ||
363 | temp_str = new LLString(); | ||
364 | if (object->flagScripted()) | ||
365 | { | ||
366 | temp_str->append("Script "); | ||
367 | } | ||
368 | |||
369 | if (object->usePhysics()) | ||
370 | { | ||
371 | temp_str->append("Physics "); | ||
372 | } | ||
373 | |||
374 | if (object->flagHandleTouch() || (parent && parent->flagHandleTouch()) ) | ||
375 | { | ||
376 | temp_str->append("Touch "); | ||
377 | suppressObjectHoverDisplay = FALSE; // Show tip | ||
378 | } | ||
379 | |||
380 | if (object->flagTakesMoney() || (parent && parent->flagTakesMoney()) ) | ||
381 | { | ||
382 | temp_str->append("Money "); | ||
383 | suppressObjectHoverDisplay = FALSE; // Show tip | ||
384 | } | ||
385 | |||
386 | if (object->flagAllowInventoryAdd()) | ||
387 | { | ||
388 | temp_str->append("Drop Inventory "); | ||
389 | suppressObjectHoverDisplay = FALSE; // Show tip | ||
390 | } | ||
391 | |||
392 | if (object->flagPhantom()) | ||
393 | { | ||
394 | temp_str->append("Phantom "); | ||
395 | } | ||
396 | |||
397 | if (object->flagTemporary()) | ||
398 | { | ||
399 | temp_str->append("Temporary "); | ||
400 | } | ||
401 | |||
402 | if (object->usePhysics() || | ||
403 | object->flagHandleTouch() || | ||
404 | (parent && parent->flagHandleTouch()) ) | ||
405 | { | ||
406 | temp_str->append("(Right-click for menu) "); | ||
407 | } | ||
408 | mText.addDataAtEnd(temp_str); | ||
409 | } | ||
410 | |||
411 | if (nodep->mValid) | ||
412 | { | ||
413 | BOOL for_copy = nodep->mPermissions->getMaskEveryone() & PERM_COPY && object->permCopy(); | ||
414 | BOOL for_sale = nodep->mSaleInfo.isForSale() && | ||
415 | nodep->mPermissions->getMaskOwner() & PERM_TRANSFER && | ||
416 | (nodep->mPermissions->getMaskOwner() & PERM_COPY || | ||
417 | nodep->mSaleInfo.getSaleType() != LLSaleInfo::FS_COPY); | ||
418 | if (for_copy) | ||
419 | { | ||
420 | temp_str = new LLString(); | ||
421 | temp_str->append("Free to copy"); | ||
422 | mText.addDataAtEnd(temp_str); | ||
423 | suppressObjectHoverDisplay = FALSE; // Show tip | ||
424 | } | ||
425 | else if (for_sale) | ||
426 | { | ||
427 | temp_str = new LLString(); | ||
428 | temp_str->append("For Sale: "); | ||
429 | sprintf(cstring, "L$%d", nodep->mSaleInfo.getSalePrice()); | ||
430 | temp_str->append(cstring); | ||
431 | mText.addDataAtEnd(temp_str); | ||
432 | suppressObjectHoverDisplay = FALSE; // Show tip | ||
433 | } | ||
434 | else | ||
435 | { | ||
436 | // Nothing if not for sale | ||
437 | // temp_str = new LLString(); | ||
438 | // temp_str->append("Not for sale"); | ||
439 | } | ||
440 | } | ||
441 | else | ||
442 | { | ||
443 | temp_str = new LLString(); | ||
444 | temp_str->append("For Sale: Retrieving..."); | ||
445 | mText.addDataAtEnd(temp_str); | ||
446 | } | ||
447 | } | ||
448 | // If the hover tip shouldn't be shown, delete all the object text | ||
449 | if (suppressObjectHoverDisplay) | ||
450 | { | ||
451 | mText.deleteAllData(); | ||
452 | } | ||
453 | } | ||
454 | } | ||
455 | else if ( mHoverLandGlobal != LLVector3d::zero ) | ||
456 | { | ||
457 | |||
458 | // | ||
459 | // Do not show hover for land unless prefs are set to allow it. | ||
460 | // | ||
461 | |||
462 | if (!gSavedSettings.getBOOL("ShowLandHoverTip")) return; | ||
463 | |||
464 | // Didn't hit an object, but since we have a land point we | ||
465 | // must be hovering over land. | ||
466 | LLString *line = NULL; | ||
467 | |||
468 | LLParcel* hover_parcel = gParcelMgr->getHoverParcel(); | ||
469 | LLUUID owner; | ||
470 | S32 width = 0; | ||
471 | S32 height = 0; | ||
472 | |||
473 | if ( hover_parcel ) | ||
474 | { | ||
475 | owner = hover_parcel->getOwnerID(); | ||
476 | width = S32(gParcelMgr->getHoverParcelWidth()); | ||
477 | height = S32(gParcelMgr->getHoverParcelHeight()); | ||
478 | } | ||
479 | |||
480 | // Line: "Land" | ||
481 | line = new LLString(); | ||
482 | mText.addDataAtEnd(line); | ||
483 | |||
484 | line->append("Land: "); | ||
485 | if (hover_parcel) | ||
486 | { | ||
487 | line->append(hover_parcel->getName()); | ||
488 | } | ||
489 | |||
490 | // Line: "Owner: James Linden" | ||
491 | line = new LLString(); | ||
492 | mText.addDataAtEnd(line); | ||
493 | |||
494 | line->append("Owner: "); | ||
495 | |||
496 | if ( hover_parcel ) | ||
497 | { | ||
498 | if (LLUUID::null == owner) | ||
499 | { | ||
500 | line->append("Public"); | ||
501 | } | ||
502 | else if (hover_parcel->getIsGroupOwned()) | ||
503 | { | ||
504 | if (gCacheName->getGroupName(owner, group_name)) | ||
505 | { | ||
506 | line->append(group_name); | ||
507 | line->append("(Group)"); | ||
508 | } | ||
509 | else | ||
510 | { | ||
511 | line->append("Retrieving..."); | ||
512 | } | ||
513 | } | ||
514 | else if(gCacheName->getName(owner, first_name, last_name)) | ||
515 | { | ||
516 | line->append(first_name); | ||
517 | line->append(" "); | ||
518 | line->append(last_name); | ||
519 | } | ||
520 | else | ||
521 | { | ||
522 | line->append("Retrieving..."); | ||
523 | } | ||
524 | } | ||
525 | else | ||
526 | { | ||
527 | line->append("Retrieving..."); | ||
528 | } | ||
529 | |||
530 | // Line: "no fly, not safe, no build" | ||
531 | |||
532 | // Don't display properties for your land. This is just | ||
533 | // confusing, because you can do anything on your own land. | ||
534 | if ( hover_parcel && owner != gAgent.getID() ) | ||
535 | { | ||
536 | S32 words = 0; | ||
537 | line = new LLString(""); | ||
538 | |||
539 | // JC - Keep this in the same order as the checkboxes | ||
540 | // on the land info panel | ||
541 | if ( !hover_parcel->getAllowModify() ) | ||
542 | { | ||
543 | if (words) line->append(", "); | ||
544 | if ( hover_parcel->getAllowGroupModify() ) | ||
545 | { | ||
546 | line->append("Group Build"); | ||
547 | } | ||
548 | else | ||
549 | { | ||
550 | line->append("No Build"); | ||
551 | } | ||
552 | |||
553 | words++; | ||
554 | } | ||
555 | |||
556 | if ( !hover_parcel->getAllowTerraform() ) | ||
557 | { | ||
558 | if (words) line->append(", "); | ||
559 | line->append("No Edit"); | ||
560 | words++; | ||
561 | } | ||
562 | |||
563 | if ( hover_parcel->getAllowDamage() ) | ||
564 | { | ||
565 | if (words) line->append(", "); | ||
566 | line->append("Not Safe"); | ||
567 | words++; | ||
568 | } | ||
569 | |||
570 | // Maybe we should reflect the estate's block fly bit here as well? DK 12/1/04 | ||
571 | if ( !hover_parcel->getAllowFly() ) | ||
572 | { | ||
573 | if (words) line->append(", "); | ||
574 | line->append("No Fly"); | ||
575 | words++; | ||
576 | } | ||
577 | |||
578 | if ( !hover_parcel->getAllowOtherScripts() ) | ||
579 | { | ||
580 | if (words) line->append(", "); | ||
581 | if ( hover_parcel->getAllowGroupScripts() ) | ||
582 | { | ||
583 | line->append("Group Scripts"); | ||
584 | } | ||
585 | else | ||
586 | { | ||
587 | line->append("No Scripts"); | ||
588 | } | ||
589 | |||
590 | words++; | ||
591 | } | ||
592 | |||
593 | if (words) | ||
594 | { | ||
595 | mText.addDataAtEnd(line); | ||
596 | } | ||
597 | else | ||
598 | { | ||
599 | delete line; | ||
600 | line = NULL; | ||
601 | } | ||
602 | } | ||
603 | |||
604 | // Line: "Size: 1x4" | ||
605 | // Only show for non-public land | ||
606 | /* | ||
607 | if ( hover_parcel && LLUUID::null != owner) | ||
608 | { | ||
609 | line = new LLString(); | ||
610 | mText.addDataAtEnd(line); | ||
611 | |||
612 | char buffer[MAX_STRING]; | ||
613 | sprintf(buffer, "Size: %dx%d", width, height ); | ||
614 | line->append(buffer); | ||
615 | } | ||
616 | */ | ||
617 | if (hover_parcel && hover_parcel->getParcelFlag(PF_FOR_SALE)) | ||
618 | { | ||
619 | char buffer[MAX_STRING]; | ||
620 | sprintf(buffer, "For Sale: L$%d", hover_parcel->getSalePrice() ); | ||
621 | |||
622 | line = new LLString(buffer); | ||
623 | mText.addDataAtEnd(line); | ||
624 | } | ||
625 | } | ||
626 | } | ||
627 | |||
628 | |||
629 | void LLHoverView::draw() | ||
630 | { | ||
631 | if( !getVisible() ) | ||
632 | { | ||
633 | return; | ||
634 | } | ||
635 | |||
636 | if ( !isHovering() ) | ||
637 | { | ||
638 | return; | ||
639 | } | ||
640 | |||
641 | // To toggle off hover tips, you have to just suppress the draw. | ||
642 | // The picking is still needed to do cursor changes over physical | ||
643 | // and scripted objects. JC | ||
644 | if (!sShowHoverTips) return; | ||
645 | |||
646 | const F32 MAX_HOVER_DISPLAY_SECS = 5.f; | ||
647 | if (mHoverTimer.getElapsedTimeF32() > MAX_HOVER_DISPLAY_SECS) | ||
648 | { | ||
649 | return; | ||
650 | } | ||
651 | |||
652 | const F32 MAX_ALPHA = 0.9f; | ||
653 | //const F32 STEADY_ALPHA = 0.3f; | ||
654 | F32 alpha; | ||
655 | if (mHoverActive) | ||
656 | { | ||
657 | alpha = 1.f; | ||
658 | |||
659 | if (isHoveringObject()) | ||
660 | { | ||
661 | // look at object | ||
662 | LLViewerObject *hover_object = getLastHoverObject(); | ||
663 | if (hover_object->isAvatar()) | ||
664 | { | ||
665 | gAgent.setLookAt(LOOKAT_TARGET_HOVER, getLastHoverObject(), LLVector3::zero); | ||
666 | } | ||
667 | else | ||
668 | { | ||
669 | LLVector3 local_offset((F32)mHoverOffset.mdV[VX], (F32)mHoverOffset.mdV[VY], (F32)mHoverOffset.mdV[VZ]); | ||
670 | gAgent.setLookAt(LOOKAT_TARGET_HOVER, getLastHoverObject(), local_offset); | ||
671 | } | ||
672 | } | ||
673 | } | ||
674 | else | ||
675 | { | ||
676 | alpha = llmax(0.f, MAX_ALPHA - mHoverTimer.getElapsedTimeF32()*2.f); | ||
677 | } | ||
678 | |||
679 | // Bail out if no text to display | ||
680 | if (mText.isEmpty()) | ||
681 | { | ||
682 | return; | ||
683 | } | ||
684 | |||
685 | // Don't draw if no alpha | ||
686 | if (alpha <= 0.f) | ||
687 | { | ||
688 | return; | ||
689 | } | ||
690 | |||
691 | LLViewerImage* box_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square.tga")), MIPMAP_FALSE, TRUE); | ||
692 | LLViewerImage* shadow_imagep = gImageList.getImage(LLUUID(gViewerArt.getString("rounded_square_soft.tga")), MIPMAP_FALSE, TRUE); | ||
693 | |||
694 | const LLFontGL* fontp = gResMgr->getRes(LLFONT_SANSSERIF_SMALL); | ||
695 | |||
696 | // Render text. | ||
697 | LLColor4 text_color = gColors.getColor("ToolTipTextColor"); | ||
698 | // LLColor4 border_color = gColors.getColor("ToolTipBorderColor"); | ||
699 | LLColor4 bg_color = gColors.getColor("ToolTipBgColor"); | ||
700 | LLColor4 shadow_color = gColors.getColor("ColorDropShadow"); | ||
701 | |||
702 | // Could decrease the alpha here. JC | ||
703 | //text_color.mV[VALPHA] = alpha; | ||
704 | //border_color.mV[VALPHA] = alpha; | ||
705 | //bg_color.mV[VALPHA] = alpha; | ||
706 | |||
707 | S32 max_width = 0; | ||
708 | S32 num_lines = mText.getLength(); | ||
709 | LLString *cur_stringp; | ||
710 | for (cur_stringp = mText.getFirstData(); cur_stringp; cur_stringp = mText.getNextData()) | ||
711 | { | ||
712 | max_width = llmax(max_width, (S32)fontp->getWidth(*cur_stringp)); | ||
713 | } | ||
714 | |||
715 | S32 left = mHoverPos.mX + 10; | ||
716 | S32 top = mHoverPos.mY - 16; | ||
717 | S32 right = mHoverPos.mX + max_width + 30; | ||
718 | S32 bottom = mHoverPos.mY - 24 - llfloor(num_lines*fontp->getLineHeight()); | ||
719 | |||
720 | // Push down if there's a one-click icon | ||
721 | if (mHoverActive | ||
722 | && isHoveringObject() | ||
723 | && mLastHoverObject->getClickAction() != CLICK_ACTION_NONE) | ||
724 | { | ||
725 | const S32 CLICK_OFFSET = 10; | ||
726 | top -= CLICK_OFFSET; | ||
727 | bottom -= CLICK_OFFSET; | ||
728 | } | ||
729 | |||
730 | // Make sure the rect is completely visible | ||
731 | LLRect old_rect = mRect; | ||
732 | mRect.set( left, top, right, bottom ); | ||
733 | translateIntoRect( gViewerWindow->getVirtualWindowRect(), FALSE ); | ||
734 | left = mRect.mLeft; | ||
735 | top = mRect.mTop; | ||
736 | right = mRect.mRight; | ||
737 | bottom = mRect.mBottom; | ||
738 | mRect = old_rect; | ||
739 | |||
740 | LLGLSUIDefault gls_ui; | ||
741 | |||
742 | shadow_color.mV[VALPHA] = 0.7f * alpha; | ||
743 | S32 shadow_offset = gSavedSettings.getS32("DropShadowTooltip"); | ||
744 | glColor4fv(shadow_color.mV); | ||
745 | LLViewerImage::bindTexture(shadow_imagep); | ||
746 | gl_segmented_rect_2d_tex(left + shadow_offset, top - shadow_offset, right + shadow_offset, bottom - shadow_offset, shadow_imagep->getWidth(), shadow_imagep->getHeight(), 16); | ||
747 | |||
748 | bg_color.mV[VALPHA] = alpha; | ||
749 | glColor4fv(bg_color.mV); | ||
750 | LLViewerImage::bindTexture(box_imagep); | ||
751 | gl_segmented_rect_2d_tex(left, top, right, bottom, box_imagep->getWidth(), box_imagep->getHeight(), 16); | ||
752 | |||
753 | S32 cur_offset = top - 4; | ||
754 | for (cur_stringp = mText.getFirstData(); cur_stringp; cur_stringp = mText.getNextData()) | ||
755 | { | ||
756 | fontp->renderUTF8(*cur_stringp, 0, left + 10, cur_offset, text_color, LLFontGL::LEFT, LLFontGL::TOP); | ||
757 | cur_offset -= llfloor(fontp->getLineHeight()); | ||
758 | } | ||
759 | } | ||
760 | |||
761 | void LLHoverView::setHoverActive(const BOOL active) | ||
762 | { | ||
763 | if (active != mHoverActive) | ||
764 | { | ||
765 | mHoverTimer.reset(); | ||
766 | } | ||
767 | |||
768 | mHoverActive = active; | ||
769 | |||
770 | if (active) | ||
771 | { | ||
772 | mHoverPos = gViewerWindow->getCurrentMouse(); | ||
773 | } | ||
774 | } | ||
775 | |||
776 | |||
777 | BOOL LLHoverView::isHoveringLand() const | ||
778 | { | ||
779 | return !mHoverLandGlobal.isExactlyZero(); | ||
780 | } | ||
781 | |||
782 | |||
783 | BOOL LLHoverView::isHoveringObject() const | ||
784 | { | ||
785 | return !mLastHoverObject.isNull() && !mLastHoverObject->isDead(); | ||
786 | } | ||
787 | |||
788 | |||
789 | LLViewerObject* LLHoverView::getLastHoverObject() const | ||
790 | { | ||
791 | if (!mLastHoverObject.isNull() && !mLastHoverObject->isDead()) | ||
792 | { | ||
793 | return mLastHoverObject; | ||
794 | } | ||
795 | else | ||
796 | { | ||
797 | return NULL; | ||
798 | } | ||
799 | } | ||
800 | |||
801 | // EOF | ||