void LLHUDNameTag::addLabel(const std::string& label_utf8) { LLWString wstr = utf8string_to_wstring(label_utf8); if (!wstr.empty()) { LLWString seps(utf8str_to_wstring("\r\n")); LLWString empty; typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer; boost::char_separator<llwchar> sep(seps.c_str(), empty.c_str(), boost::keep_empty_tokens); tokenizer tokens(wstr, sep); tokenizer::iterator iter = tokens.begin(); while (iter != tokens.end()) { U32 line_length = 0; do { S32 segment_length = mFontp->maxDrawableChars(iter->substr(line_length).c_str(), HUD_TEXT_MAX_WIDTH, wstr.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); LLHUDTextSegment segment(iter->substr(line_length, segment_length), LLFontGL::NORMAL, mColor, mFontp); mLabelSegments.push_back(segment); line_length += segment_length; } while (line_length != iter->size()); ++iter; } } }
bool LLAutoReplaceSettings::addEntryToList(LLWString keyword, LLWString replacement, std::string listName) { bool added = false; if ( ! keyword.empty() && ! replacement.empty() ) { bool isOneWord = true; for (S32 character = 0; isOneWord && character < keyword.size(); character++ ) { if ( ! LLWStringUtil::isPartOfWord(keyword[character]) ) { LL_WARNS("AutoReplace") << "keyword '" << wstring_to_utf8str(keyword) << "' not a single word (len "<<keyword.size()<<" '"<<character<<"')" << LL_ENDL; isOneWord = false; } } if ( isOneWord ) { bool listFound = false; for( LLSD::array_iterator list = mLists.beginArray(), endLists = mLists.endArray(); ! listFound && list != endLists; list++ ) { if ( listNameMatches(*list, listName) ) { listFound = true; (*list)[AUTOREPLACE_LIST_REPLACEMENTS][wstring_to_utf8str(keyword)]=wstring_to_utf8str(replacement); } } if (listFound) { added = true; } else { LL_WARNS("AutoReplace") << "list '" << listName << "' not found" << LL_ENDL; } } } return added; }
void LLFixedBuffer::addLine(const LLWString& line) { if (line.empty()) { return; } removeExtraLines(); mLines.push_back(line); mLineLengths.push_back((S32)line.length()); mAddTimes.push_back(mTimer.getElapsedTimeF32()); }
// <dogmode> void LLChatBar::sendChat( EChatType type ) { if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if (!text.empty()) { // store sent line in history, duplicates will get filtered if (mInputEditor) mInputEditor->updateHistory(); S32 channel = 0; stripChannelNumber(text, &channel); std::string utf8text = wstring_to_utf8str(text);//+" and read is "+llformat("%f",readChan)+" and undone is "+llformat("%d",undoneChan)+" but actualy channel is "+llformat("%d",channel); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { convert_roleplay_text(utf8text); // discard returned "found" boolean LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); EChatType nType(type == CHAT_TYPE_OOC ? CHAT_TYPE_NORMAL : type); if (!utf8_revised_text.empty() && cmd_line_chat(utf8_revised_text, nType)) { // Chat with animation #if SHY_MOD //Command handler if(!SHCommandHandler::handleCommand(true, utf8_revised_text, gAgentID, (LLViewerObject*)gAgentAvatarp))//returns true if handled #endif //shy_mod sendChatFromViewer(utf8_revised_text, nType, TRUE); } } } childSetValue("Chat Editor", LLStringUtil::null); gAgent.stopTyping(); // If the user wants to stop chatting on hitting return, lose focus // and go out of chat mode. if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn")) { stopChat(); } }
void LLNearbyChatBar::sendChat( EChatType type ) { if (mChatBox) { LLWString text = mChatBox->getConvertedText(); if (!text.empty()) { // store sent line in history, duplicates will get filtered mChatBox->updateHistory(); // Check if this is destined for another channel S32 channel = 0; stripChannelNumber(text, &channel); std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { // discard returned "found" boolean LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); type = processChatTypeTriggers(type, utf8_revised_text); if (!utf8_revised_text.empty()) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, TRUE); } } mChatBox->setText(LLStringExplicit("")); } gAgent.stopTyping(); // If the user wants to stop chatting on hitting return, lose focus // and go out of chat mode. if (gSavedSettings.getBOOL("CloseChatOnReturn")) { stopChat(); } }
void LLChatBar::sendChat( EChatType type ) { if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if (!text.empty()) { // store sent line in history, duplicates will get filtered if (mInputEditor) mInputEditor->updateHistory(); // Check if this is destined for another channel S32 channel = mChanCtrlEnabled ? (S32)(mChannelControl->get()) : 0; stripChannelNumber(text, &channel); std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { // discard returned "found" boolean gGestureManager.triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); if (!utf8_revised_text.empty()) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, TRUE); } } } childSetValue("Chat Editor", LLStringUtil::null); gAgent.stopTyping(); // If the user wants to stop chatting on hitting return, lose focus // and go out of chat mode. if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn")) { stopChat(); } }
void LLFloaterIMPanel::sendMsg() { if (!gAgent.isGodlike() && (mDialog == IM_NOTHING_SPECIAL) && mOtherParticipantUUID.isNull()) { llinfos << "Cannot send IM to everyone unless you're a god." << llendl; return; } if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if(!text.empty()) { // store sent line in history, duplicates will get filtered if (mInputEditor) mInputEditor->updateHistory(); // Truncate and convert to UTF8 for transport std::string utf8_text = wstring_to_utf8str(text); utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); if ( mSessionInitialized ) { LLIMModel::sendMessage(utf8_text, mSessionUUID, mOtherParticipantUUID, mDialog); } else { //queue up the message to send once the session is //initialized mQueuedMsgsForInit.append(utf8_text); } } LLViewerStats::getInstance()->incStat(LLViewerStats::ST_IM_COUNT); mInputEditor->setText(LLStringUtil::null); } // Don't need to actually send the typing stop message, the other // client will infer it from receiving the message. mTyping = FALSE; mSentTypingState = TRUE; }
void LLConsole::addQueuedLines() { for (line_queue_t::iterator iter = mLineQueue.begin(); iter != mLineQueue.end(); ++iter) { LineInfo& line_info = *iter; LLWString wline = line_info.wline; //F32 size = line_info.size; LLColor4 color = line_info.color; if (!wline.empty() && mFont != NULL) { // Wrap lines that are longer than the view is wide. S32 offset = 0; while( offset < (S32)wline.length() ) { S32 skip_chars; // skip '\n' // Figure out if a word-wrapped line fits here. LLWString::size_type line_end = wline.find_first_of(llwchar('\n'), offset); if (line_end != LLWString::npos) { skip_chars = 1; // skip '\n' } else { line_end = wline.size(); skip_chars = 0; } U32 drawable = mFont->maxDrawableChars(wline.c_str()+offset, (F32)mRect.getWidth(), line_end-offset, TRUE); if (drawable != 0) { LLFixedBuffer::addLine(wline.substr(offset, drawable)); mAddTimes[mAddTimes.size()-1] = line_info.add_time; } else { // force a blank line LLFixedBuffer::addLine(" "); } mColors.push_back(color); offset += (drawable + skip_chars); } } } mLineQueue.clear(); }
void LLIMFloater::sendMsg() { if (!gAgent.isGodlike() && (mDialog == IM_NOTHING_SPECIAL) && mOtherParticipantUUID.isNull()) { llinfos << "Cannot send IM to everyone unless you're a god." << llendl; return; } if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if(!text.empty()) { // Truncate and convert to UTF8 for transport std::string utf8_text = wstring_to_utf8str(text); utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); if (mSessionInitialized) { LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID,mDialog); } else { //queue up the message to send once the session is initialized mQueuedMsgsForInit.append(utf8_text); } mInputEditor->setText(LLStringUtil::null); updateMessages(); } else if (gSavedSettings.getBOOL("CloseIMOnEmptyReturn")) { // Close if we're the child of a floater closeFloater(); } } }
void LLHUDText::addLine(const std::string &text_utf8, const LLColor4& color, const LLFontGL::StyleFlags style, const LLFontGL* font) { LLWString wline = utf8str_to_wstring(text_utf8); if (!wline.empty()) { // use default font for segment if custom font not specified if (!font) { font = mFontp; } typedef boost::tokenizer<boost::char_separator<llwchar>, LLWString::const_iterator, LLWString > tokenizer; LLWString seps(utf8str_to_wstring("\r\n")); boost::char_separator<llwchar> sep(seps.c_str()); tokenizer tokens(wline, sep); tokenizer::iterator iter = tokens.begin(); while (iter != tokens.end()) { U32 line_length = 0; do { F32 max_pixels = HUD_TEXT_MAX_WIDTH_NO_BUBBLE; S32 segment_length = font->maxDrawableChars(iter->substr(line_length).c_str(), max_pixels, wline.length(), LLFontGL::WORD_BOUNDARY_IF_POSSIBLE); LLHUDTextSegment segment(iter->substr(line_length, segment_length), style, color, font); mTextSegments.push_back(segment); line_length += segment_length; } while (line_length != iter->size()); ++iter; } } }
void hud_render_text(const LLWString &wstr, const LLVector3 &pos_agent, const LLFontGL &font, const U8 style, const LLFontGL::ShadowType shadow, const F32 x_offset, const F32 y_offset, const LLColor4& color, const BOOL orthographic) { LLViewerCamera* camera = LLViewerCamera::getInstance(); // Do cheap plane culling LLVector3 dir_vec = pos_agent - camera->getOrigin(); dir_vec /= dir_vec.magVec(); if (wstr.empty() || (!orthographic && dir_vec * camera->getAtAxis() <= 0.f)) { return; } LLVector3 right_axis; LLVector3 up_axis; if (orthographic) { right_axis.setVec(0.f, -1.f / gViewerWindow->getWorldViewHeightScaled(), 0.f); up_axis.setVec(0.f, 0.f, 1.f / gViewerWindow->getWorldViewHeightScaled()); } else { camera->getPixelVectors(pos_agent, up_axis, right_axis); } LLCoordFrame render_frame = *camera; LLQuaternion rot; if (!orthographic) { rot = render_frame.getQuaternion(); rot = rot * LLQuaternion(-F_PI_BY_TWO, camera->getYAxis()); rot = rot * LLQuaternion(F_PI_BY_TWO, camera->getXAxis()); } else { rot = LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 0.f, 1.f)); rot = rot * LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 1.f, 0.f)); } F32 angle; LLVector3 axis; rot.getAngleAxis(&angle, axis); LLVector3 render_pos = pos_agent + (floorf(x_offset) * right_axis) + (floorf(y_offset) * up_axis); //get the render_pos in screen space LLVector3 window_coordinates; F32& winX = window_coordinates.mV[VX]; F32& winY = window_coordinates.mV[VY]; F32& winZ = window_coordinates.mV[VZ]; const LLRect& world_view_rect = gViewerWindow->getWorldViewRectRaw(); glProjectf(render_pos, gGLModelView, gGLProjection, world_view_rect, window_coordinates); //fonts all render orthographically, set up projection`` gGL.matrixMode(LLRender::MM_PROJECTION); gGL.pushMatrix(); gGL.matrixMode(LLRender::MM_MODELVIEW); gGL.pushMatrix(); LLUI::pushMatrix(); gl_state_for_2d(world_view_rect.getWidth(), world_view_rect.getHeight()); gViewerWindow->setup3DViewport(); winX -= world_view_rect.mLeft; winY -= world_view_rect.mBottom; LLUI::loadIdentity(); gGL.loadIdentity(); LLUI::translate((F32) winX*1.0f/LLFontGL::sScaleX, (F32) winY*1.0f/(LLFontGL::sScaleY), -(((F32) winZ*2.f)-1.f)); F32 right_x; font.render(wstr, 0, 0, 0, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, shadow, wstr.length(), 1000, &right_x); LLUI::popMatrix(); gGL.popMatrix(); gGL.matrixMode(LLRender::MM_PROJECTION); gGL.popMatrix(); gGL.matrixMode(LLRender::MM_MODELVIEW); }
// Singu Note: LLFloaterIMSession::sendMsg void LLFloaterIMPanel::onSendMsg() { if (!gAgent.isGodlike() && (mSessionType == P2P_SESSION) && mOtherParticipantUUID.isNull()) { llinfos << "Cannot send IM to everyone unless you're a god." << llendl; return; } if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if(!text.empty()) { // store sent line in history, duplicates will get filtered if (mInputEditor) mInputEditor->updateHistory(); // Truncate and convert to UTF8 for transport std::string utf8_text = wstring_to_utf8str(text); bool action = convert_roleplay_text(utf8_text); if (!action && mRPMode) utf8_text = "((" + utf8_text + "))"; // [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0) if ( (RlvActions::hasBehaviour(RLV_BHVR_SENDIM)) || (RlvActions::hasBehaviour(RLV_BHVR_SENDIMTO)) ) { bool fRlvFilter = false; switch (mSessionType) { case P2P_SESSION: // One-on-one IM fRlvFilter = !RlvActions::canSendIM(mOtherParticipantUUID); break; case GROUP_SESSION: // Group chat fRlvFilter = !RlvActions::canSendIM(mSessionUUID); break; case ADHOC_SESSION: // Conference chat: allow if all participants can be sent an IM { if (!mSpeakers) { fRlvFilter = true; break; } LLSpeakerMgr::speaker_list_t speakers; mSpeakers->getSpeakerList(&speakers, TRUE); for (LLSpeakerMgr::speaker_list_t::const_iterator itSpeaker = speakers.begin(); itSpeaker != speakers.end(); ++itSpeaker) { const LLSpeaker* pSpeaker = *itSpeaker; if ( (gAgentID != pSpeaker->mID) && (!RlvActions::canSendIM(pSpeaker->mID)) ) { fRlvFilter = true; break; } } } break; default: fRlvFilter = true; break; } if (fRlvFilter) { utf8_text = RlvStrings::getString(RLV_STRING_BLOCKED_SENDIM); } } // [/RLVa:KB] if ( mSessionInitialized ) { // Split messages that are too long, same code like in llimpanel.cpp U32 split = MAX_MSG_BUF_SIZE - 1; U32 pos = 0; U32 total = utf8_text.length(); while (pos < total) { U32 next_split = split; if (pos + next_split > total) { next_split = total - pos; } else { // don't split utf-8 bytes while (U8(utf8_text[pos + next_split]) != 0x20 // space && U8(utf8_text[pos + next_split]) != 0x21 // ! && U8(utf8_text[pos + next_split]) != 0x2C // , && U8(utf8_text[pos + next_split]) != 0x2E // . && U8(utf8_text[pos + next_split]) != 0x3F // ? && next_split > 0) { --next_split; } if (next_split == 0) { next_split = split; LL_WARNS("Splitting") << "utf-8 couldn't be split correctly" << LL_ENDL; } else { ++next_split; } } std::string send = utf8_text.substr(pos, next_split); pos += next_split; LL_DEBUGS("Splitting") << "Pos: " << pos << " next_split: " << next_split << LL_ENDL; deliver_message(send, mSessionUUID, mOtherParticipantUUID, mDialog); } // local echo if((mSessionType == P2P_SESSION) && (mOtherParticipantUUID.notNull())) { std::string name; gAgent.buildFullname(name); // Look for actions here. if (action) { utf8_text.replace(0,3,""); } else { utf8_text.insert(0, ": "); } bool other_was_typing = mOtherTyping; addHistoryLine(utf8_text, gSavedSettings.getColor("UserChatColor"), true, gAgentID, name); if (other_was_typing) addTypingIndicator(mOtherTypingName); } } else { //queue up the message to send once the session is //initialized mQueuedMsgsForInit.append(utf8_text); } } LLViewerStats::getInstance()->incStat(LLViewerStats::ST_IM_COUNT); mInputEditor->setText(LLStringUtil::null); } // Don't need to actually send the typing stop message, the other // client will infer it from receiving the message. mTyping = false; mSentTypingState = true; }
S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { LLFastTimer _(FTM_RENDER_FONTS); if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty() || !max_pixels) { return 0; } if (max_chars == -1) max_chars = S32_MAX; const S32 max_index = llmin(llmax(max_chars, begin_offset + max_chars), S32(wstr.length())); if (max_index <= 0 || begin_offset >= max_index || max_pixels <= 0) return 0; gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // Strip off any style bits that are already accounted for by the font. style = style & (~getFontDesc().getStyle()); F32 drop_shadow_strength = 0.f; if (shadow != NO_SHADOW) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { shadow = NO_SHADOW; } } gGL.pushUIMatrix(); gGL.loadUIIdentity(); LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY)); // Depth translation, so that floating text appears 'in-world' // and is correctly occluded. gGL.translatef(0.f,0.f,sCurDepth); S32 chars_drawn = 0; S32 i; S32 length = max_index - begin_offset; F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX) + origin.mV[VX]; cur_y = ((F32)y * sScaleY) + origin.mV[VY]; // Offset y by vertical alignment. // use unscaled font metrics here switch (valign) { case TOP: cur_y -= llceil(mFontFreetype->getAscenderHeight()); break; case BOTTOM: cur_y += llceil(mFontFreetype->getDescenderHeight()); break; case VCENTER: cur_y -= llceil((llceil(mFontFreetype->getAscenderHeight()) - llceil(mFontFreetype->getDescenderHeight())) / 2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, ll_round(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = (F32)ll_round(cur_x); const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string S32 string_width = ll_round(getWidthF32(wstr, begin_offset, max_chars) * sScaleX); if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - ll_round(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } const LLFontGlyphInfo* next_glyph = NULL; const S32 GLYPH_BATCH_SIZE = 30; static LL_ALIGN_16(LLVector4a vertices[GLYPH_BATCH_SIZE * 4]); static LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; static LLColor4U colors[GLYPH_BATCH_SIZE * 4]; LLColor4U text_color(color); S32 bitmap_num = -1; S32 glyph_count = 0; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } gGL.getTexUnit(0)->bind(ext_image); // snap origin to whole screen pixel const F32 ext_x = (F32)ll_round(cur_render_x + (EXT_X_BEARING * sScaleX)); const F32 ext_y = (F32)ll_round(cur_render_y + (EXT_Y_BEARING * sScaleY + mFontFreetype->getAscenderHeight() - mFontFreetype->getLineHeight())); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); if (glyph_count > 0) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } renderQuad(vertices, uvs, colors, screen_rect, uv_rect, LLColor4U::white, 0); //No batching here. It will never happen. gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, 4); } gGL.end(); if (!label.empty()) { gGL.pushMatrix(); getFontExtChar()->render(label, 0, /*llfloor*/(ext_x / sScaleX) + ext_image->getWidth() + EXT_X_BEARING - sCurOrigin.mX, /*llfloor*/(cur_render_y / sScaleY) - sCurOrigin.mY, color, halign, BASELINE, UNDERLINE, NO_SHADOW, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; } else { const LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } if (!fgi) { LL_ERRS() << "Missing Glyph Info" << LL_ENDL; break; } // Per-glyph bitmap texture. S32 next_bitmap_num = fgi->mBitmapNum; if (next_bitmap_num != bitmap_num) { // Actually draw the queued glyphs before switching their texture; // otherwise the queued glyphs will be taken from wrong textures. if (glyph_count > 0) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } bitmap_num = next_bitmap_num; LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); gGL.getTexUnit(0)->bind(font_image); } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect((F32)ll_round(cur_render_x + (F32)fgi->mXBearing), (F32)ll_round(cur_render_y + (F32)fgi->mYBearing), (F32)ll_round(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, (F32)ll_round(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); if (glyph_count >= GLYPH_BATCH_SIZE) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style, shadow, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(next_char); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)ll_round(cur_x); //cur_y = (F32)ll_round(cur_y); cur_render_x = cur_x; cur_render_y = cur_y; } } if(glyph_count) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); } if (right_x) { *right_x = (cur_x - origin.mV[VX]) / sScaleX; } if (style & UNDERLINE) { F32 descender = (F32)llfloor(mFontFreetype->getDescenderHeight()); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - descender); gGL.vertex2f(cur_x, cur_y - descender); gGL.end(); } if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); renderUTF8(std::string("..."), 0, (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, LEFT, valign, style, shadow, S32_MAX, max_pixels, right_x, FALSE); gGL.popUIMatrix(); } gGL.popUIMatrix(); return chars_drawn; }
// virtual void LLButton::draw() { F32 alpha = getDrawContext().mAlpha; bool flash = FALSE; static LLUICachedControl<F32> button_flash_rate("ButtonFlashRate", 0); static LLUICachedControl<S32> button_flash_count("ButtonFlashCount", 0); if( mFlashing ) { F32 elapsed = mFlashingTimer.getElapsedTimeF32(); S32 flash_count = S32(elapsed * button_flash_rate * 2.f); // flash on or off? flash = (flash_count % 2 == 0) || flash_count > S32((F32)button_flash_count * 2.f); } bool pressed_by_keyboard = FALSE; if (hasFocus()) { pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); } // Unselected image assignments bool enabled = isInEnabledChain(); bool pressed = pressed_by_keyboard || (hasMouseCapture() && mMouseOver) || mForcePressedState; bool selected = getToggleState(); bool use_glow_effect = FALSE; LLColor4 glow_color = LLColor4::white; LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; LLUIImage* imagep = NULL; if (pressed) { imagep = selected ? mImagePressedSelected : mImagePressed; } else if ( mNeedsHighlight ) { if (selected) { if (mImageHoverSelected) { imagep = mImageHoverSelected; } else { imagep = mImageSelected; use_glow_effect = TRUE; } } else { if (mImageHoverUnselected) { imagep = mImageHoverUnselected; } else { imagep = mImageUnselected; use_glow_effect = TRUE; } } } else { imagep = selected ? mImageSelected : mImageUnselected; } // Override if more data is available // HACK: Use gray checked state to mean either: // enabled and tentative // or // disabled but checked if (!mImageDisabledSelected.isNull() && ( (enabled && getTentative()) || (!enabled && selected ) ) ) { imagep = mImageDisabledSelected; } else if (!mImageDisabled.isNull() && !enabled && !selected) { imagep = mImageDisabled; } if (mFlashing) { // if button should flash and we have icon for flashing, use it as image for button if(flash && mImageFlash) { // setting flash to false to avoid its further influence on glow flash = false; imagep = mImageFlash; } // else use usual flashing via flash_color else { LLColor4 flash_color = mFlashBgColor.get(); use_glow_effect = TRUE; glow_type = LLRender::BT_ALPHA; // blend the glow if (mNeedsHighlight) // highlighted AND flashing glow_color = (glow_color*0.5f + flash_color*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity else glow_color = flash_color; } } if (mNeedsHighlight && !imagep) { use_glow_effect = TRUE; } // Figure out appropriate color for the text LLColor4 label_color; // label changes when button state changes, not when pressed if ( enabled ) { if ( getToggleState() ) { label_color = mSelectedLabelColor.get(); } else { label_color = mUnselectedLabelColor.get(); } } else { if ( getToggleState() ) { label_color = mDisabledSelectedLabelColor.get(); } else { label_color = mDisabledLabelColor.get(); } } // Unselected label assignments LLWString label; if( getToggleState() ) { label = mSelectedLabel; } else { label = mUnselectedLabel; } // overlay with keyboard focus border if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, llround(lerp(1.f, 3.f, lerp_amt))); } if (use_glow_effect) { mCurGlowStrength = lerp(mCurGlowStrength, mFlashing ? (flash? 1.0 : 0.0) : mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f)); } else { mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f)); } // Draw button image, if available. // Otherwise draw basic rectangular button. if (imagep != NULL) { // apply automatic 50% alpha fade to disabled image LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get(); if ( mScaleImage) { imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } else { imagep->draw(0, 0, (enabled ? mImageColor.get() : disabled_color) % alpha ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); imagep->drawSolid(0, 0, glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } } else { // no image lldebugs << "No image for button " << getName() << llendl; // draw it in pink so we can find it gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE); } // let overlay image and text play well together S32 text_left = mLeftHPad; S32 text_right = getRect().getWidth() - mRightHPad; S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad; // draw overlay image if (mImageOverlay.notNull()) { // get max width and height (discard level 0) S32 overlay_width = mImageOverlay->getWidth(); S32 overlay_height = mImageOverlay->getHeight(); F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f); overlay_width = llround((F32)overlay_width * scale_factor); overlay_height = llround((F32)overlay_height * scale_factor); S32 center_x = getLocalRect().getCenterX(); S32 center_y = getLocalRect().getCenterY(); //FUGLY HACK FOR "DEPRESSED" BUTTONS if (pressed) { center_y--; center_x++; } center_y += (mImageOverlayBottomPad - mImageOverlayTopPad); // fade out overlay images on disabled buttons LLColor4 overlay_color = mImageOverlayColor.get(); if (!enabled) { overlay_color.mV[VALPHA] = 0.5f; } overlay_color.mV[VALPHA] *= alpha; switch(mImageOverlayAlignment) { case LLFontGL::LEFT: text_left += overlay_width + mImgOverlayLabelSpace; mImageOverlay->draw( mLeftHPad, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::HCENTER: mImageOverlay->draw( center_x - (overlay_width / 2), center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::RIGHT: text_right -= overlay_width + mImgOverlayLabelSpace; mImageOverlay->draw( getRect().getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; default: // draw nothing break; } } // Draw label if( !label.empty() ) { LLWStringUtil::trim(label); S32 x; switch( mHAlign ) { case LLFontGL::RIGHT: x = text_right; break; case LLFontGL::HCENTER: x = text_left + (text_width / 2); break; case LLFontGL::LEFT: default: x = text_left; break; } S32 y_offset = 2 + (getRect().getHeight() - 20)/2; if (pressed) { y_offset--; x++; } // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as // max_chars. // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value. // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode. // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars. mLastDrawCharsCount = mGLFont->render(label, 0, (F32)x, (F32)(mBottomVPad + y_offset), label_color % alpha, mHAlign, LLFontGL::BOTTOM, LLFontGL::NORMAL, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW, S32_MAX, text_width, NULL, mUseEllipses); } LLUICtrl::draw(); }
void LLChatBar::sendChat( EChatType type ) { if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if (!text.empty()) { // store sent line in history, duplicates will get filtered if (mInputEditor) mInputEditor->updateHistory(); // Check if this is destined for another channel S32 channel = mChanCtrlEnabled ? (S32)(mChannelControl->get()) : 0; stripChannelNumber(text, &channel); std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { if (gSavedSettings.getBOOL("AutoCloseOOC")) { // Try to find any unclosed OOC chat (i.e. an opening // double parenthesis without a matching closing double // parenthesis. if (utf8text.find("((") != -1 && utf8text.find("))") == -1) { if (utf8text.at(utf8text.length() - 1) == ')') { // cosmetic: add a space first to avoid a closing triple parenthesis utf8text += " "; } // add the missing closing double parenthesis. utf8text += "))"; } } // Convert MU*s style poses into IRC emotes here. if (gSavedSettings.getBOOL("AllowMUpose") && utf8text.find(":") == 0 && utf8text.length() > 3) { if (utf8text.find(":'") == 0) { utf8text.replace(0, 1, "/me"); } else if (isalpha(utf8text.at(1))) // Do not prevent smileys and such. { utf8text.replace(0, 1, "/me "); } } // discard returned "found" boolean gGestureManager.triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); if (!utf8_revised_text.empty()) if (!utf8_revised_text.empty() && cmd_line_chat(utf8_revised_text, type)) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, TRUE); } } } childSetValue("Chat Editor", LLStringUtil::null); gAgent.stopTyping(); // If the user wants to stop chatting on hitting return, lose focus // and go out of chat mode. if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn")) { stopChat(); } }
// virtual void LLButton::draw() { static LLCachedControl<bool> sEnableButtonFlashing(*LLUI::sSettingGroups["config"], "EnableButtonFlashing", true); F32 alpha = mUseDrawContextAlpha ? getDrawContext().mAlpha : getCurrentTransparency(); bool pressed_by_keyboard = FALSE; if (hasFocus()) { pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); } bool mouse_pressed_and_over = false; if (hasMouseCapture()) { S32 local_mouse_x ; S32 local_mouse_y; LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); mouse_pressed_and_over = pointInView(local_mouse_x, local_mouse_y); } bool enabled = isInEnabledChain(); bool pressed = pressed_by_keyboard || mouse_pressed_and_over || mForcePressedState; bool selected = getToggleState(); bool use_glow_effect = FALSE; LLColor4 highlighting_color = LLColor4::white; LLColor4 glow_color = LLColor4::white; LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; LLUIImage* imagep = NULL; // Cancel sticking of color, if the button is pressed, // or when a flashing of the previously selected button is ended if (mFlashingTimer && ((selected && !mFlashingTimer->isFlashingInProgress() && !mForceFlashing) || pressed)) { mFlashing = false; } bool flash = mFlashing && sEnableButtonFlashing; if (pressed && mDisplayPressedState) { imagep = selected ? mImagePressedSelected : mImagePressed; } else if ( mNeedsHighlight ) { if (selected) { if (mImageHoverSelected) { imagep = mImageHoverSelected; } else { imagep = mImageSelected; use_glow_effect = TRUE; } } else { if (mImageHoverUnselected) { imagep = mImageHoverUnselected; } else { imagep = mImageUnselected; use_glow_effect = TRUE; } } } else { imagep = selected ? mImageSelected : mImageUnselected; } // Override if more data is available // HACK: Use gray checked state to mean either: // enabled and tentative // or // disabled but checked if (!mImageDisabledSelected.isNull() && ( (enabled && getTentative()) || (!enabled && selected ) ) ) { imagep = mImageDisabledSelected; } else if (!mImageDisabled.isNull() && !enabled && !selected) { imagep = mImageDisabled; } if (mFlashing) { // if button should flash and we have icon for flashing, use it as image for button if(flash && mImageFlash) { // setting flash to false to avoid its further influence on glow flash = false; imagep = mImageFlash; } // else use usual flashing via flash_color else if (mFlashingTimer) { LLColor4 flash_color = mFlashBgColor.get(); use_glow_effect = TRUE; glow_type = LLRender::BT_ALPHA; // blend the glow if (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress()) { glow_color = flash_color; } else if (mNeedsHighlight) { glow_color = highlighting_color; } } } if (mNeedsHighlight && !imagep) { use_glow_effect = TRUE; } // Figure out appropriate color for the text LLColor4 label_color; // label changes when button state changes, not when pressed if ( enabled ) { if ( getToggleState() ) { label_color = mSelectedLabelColor.get(); } else { label_color = mUnselectedLabelColor.get(); } } else { if ( getToggleState() ) { label_color = mDisabledSelectedLabelColor.get(); } else { label_color = mDisabledLabelColor.get(); } } // <FS::ND> Highlight if needed if( nd::ui::SearchableControl::getHighlighted() ) label_color = nd::ui::SearchableControl::getHighlightColor(); // </FS:ND> // Unselected label assignments LLWString label = getCurrentLabel(); // overlay with keyboard focus border if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); drawBorder(imagep, gFocusMgr.getFocusColor() % alpha, ll_round(lerp(1.f, 3.f, lerp_amt))); } if (use_glow_effect) { mCurGlowStrength = lerp(mCurGlowStrength, // <FS:Ansariel> Crash fix; Calling setFlashing can cause mFlashing being true while is mFlashingTimer is NULL //mFlashing ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.0 : 0.0) : mHoverGlowStrength, (mFlashing && mFlashingTimer) ? (mFlashingTimer->isCurrentlyHighlighted() || !mFlashingTimer->isFlashingInProgress() || mNeedsHighlight? 1.0 : 0.0) : mHoverGlowStrength, LLSmoothInterpolation::getInterpolant(0.05f)); } else { mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLSmoothInterpolation::getInterpolant(0.05f)); } // Draw button image, if available. // Otherwise draw basic rectangular button. if (imagep != NULL) { // apply automatic 50% alpha fade to disabled image LLColor4 disabled_color = mFadeWhenDisabled ? mDisabledImageColor.get() % 0.5f : mDisabledImageColor.get(); if ( mScaleImage) { imagep->draw(getLocalRect(), (enabled ? mImageColor.get() : disabled_color) % alpha ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); imagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } else { imagep->draw(0, 0, (enabled ? mImageColor.get() : disabled_color) % alpha ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); imagep->drawSolid(0, 0, glow_color % (mCurGlowStrength * alpha)); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } } else { // no image LL_DEBUGS() << "No image for button " << getName() << LL_ENDL; // draw it in pink so we can find it gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1 % alpha, FALSE); } // let overlay image and text play well together S32 text_left = mLeftHPad; S32 text_right = getRect().getWidth() - mRightHPad; S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad; // draw overlay image if (mImageOverlay.notNull()) { // get max width and height (discard level 0) S32 overlay_width; S32 overlay_height; getOverlayImageSize(overlay_width, overlay_height); S32 center_x = getLocalRect().getCenterX(); S32 center_y = getLocalRect().getCenterY(); //FUGLY HACK FOR "DEPRESSED" BUTTONS if (pressed && mDisplayPressedState) { center_y--; center_x++; } center_y += (mImageOverlayBottomPad - mImageOverlayTopPad); // fade out overlay images on disabled buttons LLColor4 overlay_color = mImageOverlayColor.get(); if (!enabled) { overlay_color = mImageOverlayDisabledColor.get(); } else if (getToggleState()) { overlay_color = mImageOverlaySelectedColor.get(); } overlay_color.mV[VALPHA] *= alpha; switch(mImageOverlayAlignment) { case LLFontGL::LEFT: text_left += overlay_width + mImgOverlayLabelSpace; text_width -= overlay_width + mImgOverlayLabelSpace; mImageOverlay->draw( mLeftHPad, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::HCENTER: mImageOverlay->draw( center_x - (overlay_width / 2), center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::RIGHT: text_right -= overlay_width + mImgOverlayLabelSpace; text_width -= overlay_width + mImgOverlayLabelSpace; mImageOverlay->draw( getRect().getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; default: // draw nothing break; } } // Draw label if( !label.empty() ) { LLWStringUtil::trim(label); S32 x; switch( mHAlign ) { case LLFontGL::RIGHT: x = text_right; break; case LLFontGL::HCENTER: x = text_left + (text_width / 2); break; case LLFontGL::LEFT: default: x = text_left; break; } S32 y_offset = 2 + (getRect().getHeight() - 20)/2; if (pressed && mDisplayPressedState) { y_offset--; x++; } // *NOTE: mantipov: before mUseEllipses is implemented in EXT-279 U32_MAX has been passed as // max_chars. // LLFontGL::render expects S32 max_chars variable but process in a separate way -1 value. // Due to U32_MAX is equal to S32 -1 value I have rest this value for non-ellipses mode. // Not sure if it is really needed. Probably S32_MAX should be always passed as max_chars. mLastDrawCharsCount = mGLFont->render(label, 0, (F32)x, (F32)(getRect().getHeight() / 2 + mBottomVPad), label_color % alpha, mHAlign, LLFontGL::VCENTER, LLFontGL::NORMAL, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW, S32_MAX, text_width, NULL, mUseEllipses); } // <FS:Zi> Add checkbox control toggle if(mCheckboxControlPanel) { mCheckboxControlPanel->setOrigin(0,0); mCheckboxControlPanel->reshape(getRect().getWidth(),getRect().getHeight()); mCheckboxControlPanel->draw(); } // <FS:Zi> LLUICtrl::draw(); }
// virtual void LLButton::draw() { if( getVisible() ) { BOOL flash = FALSE; if( mFlashing ) { F32 elapsed = mFlashingTimer.getElapsedTimeF32(); S32 flash_count = S32(elapsed * LLUI::sConfigGroup->getF32("ButtonFlashRate") * 2.f); // flash on or off? flash = (flash_count % 2 == 0) || flash_count > (F32)LLUI::sConfigGroup->getS32("ButtonFlashCount"); } BOOL pressed_by_keyboard = FALSE; if (hasFocus()) { pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); } // Unselected image assignments S32 local_mouse_x; S32 local_mouse_y; LLCoordWindow cursor_pos_window; getWindow()->getCursorPosition(&cursor_pos_window); LLCoordGL cursor_pos_gl; getWindow()->convertCoords(cursor_pos_window, &cursor_pos_gl); cursor_pos_gl.mX = llround((F32)cursor_pos_gl.mX / LLUI::sGLScaleFactor.mV[VX]); cursor_pos_gl.mY = llround((F32)cursor_pos_gl.mY / LLUI::sGLScaleFactor.mV[VY]); screenPointToLocal(cursor_pos_gl.mX, cursor_pos_gl.mY, &local_mouse_x, &local_mouse_y); BOOL pressed = pressed_by_keyboard || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)) || mToggleState; BOOL use_glow_effect = FALSE; if ( mNeedsHighlight || flash ) { if (pressed) { if (mImageHoverSelected) { mImagep = mImageHoverSelected; } else { mImagep = mImageSelected; use_glow_effect = TRUE; } } else { if (mImageHoverUnselected) { mImagep = mImageHoverUnselected; } else { mImagep = mImageUnselected; use_glow_effect = TRUE; } } } else if ( pressed ) { mImagep = mImageSelected; } else { mImagep = mImageUnselected; } // Override if more data is available // HACK: Use gray checked state to mean either: // enabled and tentative // or // disabled but checked if (!mImageDisabledSelected.isNull() && ( (mEnabled && mTentative) || (!mEnabled && pressed ) ) ) { mImagep = mImageDisabledSelected; } else if (!mImageDisabled.isNull() && !mEnabled && !pressed) { mImagep = mImageDisabled; } if (mNeedsHighlight && !mImagep) { use_glow_effect = TRUE; } // Figure out appropriate color for the text LLColor4 label_color; // label changes when button state changes, not when pressed if ( mEnabled ) { if ( mToggleState ) { label_color = mSelectedLabelColor; } else { label_color = mUnselectedLabelColor; } } else { if ( mToggleState ) { label_color = mDisabledSelectedLabelColor; } else { label_color = mDisabledLabelColor; } } // Unselected label assignments LLWString label; if( mToggleState ) { if( mEnabled || mDisabledSelectedLabel.empty() ) { label = mSelectedLabel; } else { label = mDisabledSelectedLabel; } } else { if( mEnabled || mDisabledLabel.empty() ) { label = mUnselectedLabel; } else { label = mDisabledLabel; } } // draw default button border if (mEnabled && mBorderEnabled && gFocusMgr.getAppHasFocus()) // because we're the default button in a panel { drawBorder(LLUI::sColorsGroup->getColor( "ButtonBorderColor" ), BORDER_SIZE); } // overlay with keyboard focus border if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); drawBorder(gFocusMgr.getFocusColor(), llround(lerp(1.f, 3.f, lerp_amt))); } if (use_glow_effect) { mCurGlowStrength = lerp(mCurGlowStrength, mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f)); } else { mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f)); } // Draw button image, if available. // Otherwise draw basic rectangular button. if( mImagep.notNull() && !mScaleImage) { mImagep->draw(0, 0, mEnabled ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); mImagep->drawSolid(0, 0, LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } else if ( mImagep.notNull() && mScaleImage) { mImagep->draw(0, 0, mRect.getWidth(), mRect.getHeight(), mEnabled ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { glBlendFunc(GL_SRC_ALPHA, GL_ONE); mImagep->drawSolid(0, 0, mRect.getWidth(), mRect.getHeight(), LLColor4(1.f, 1.f, 1.f, mCurGlowStrength)); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } else { // no image llwarns << "No image for button " << mName << llendl; // draw it in pink so we can find it gl_rect_2d(0, mRect.getHeight(), mRect.getWidth(), 0, LLColor4::pink1, FALSE); } // let overlay image and text play well together S32 text_left = mLeftHPad; S32 text_right = mRect.getWidth() - mRightHPad; S32 text_width = mRect.getWidth() - mLeftHPad - mRightHPad; // draw overlay image if (mImageOverlay.notNull()) { // get max width and height (discard level 0) S32 overlay_width = mImageOverlay->getWidth(); S32 overlay_height = mImageOverlay->getHeight(); F32 scale_factor = llmin((F32)mRect.getWidth() / (F32)overlay_width, (F32)mRect.getHeight() / (F32)overlay_height, 1.f); overlay_width = llround((F32)overlay_width * scale_factor); overlay_height = llround((F32)overlay_height * scale_factor); S32 center_x = getLocalRect().getCenterX(); S32 center_y = getLocalRect().getCenterY(); //FUGLY HACK FOR "DEPRESSED" BUTTONS if (pressed) { center_y--; center_x++; } // fade out overlay images on disabled buttons LLColor4 overlay_color = mImageOverlayColor; if (!getEnabled()) { overlay_color.mV[VALPHA] = 0.5f; } switch(mImageOverlayAlignment) { case LLFontGL::LEFT: text_left += overlay_width + 1; text_width -= overlay_width + 1; mImageOverlay->draw( mLeftHPad, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::HCENTER: mImageOverlay->draw( center_x - (overlay_width / 2), center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::RIGHT: text_right -= overlay_width + 1; text_width -= overlay_width + 1; mImageOverlay->draw( mRect.getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; default: // draw nothing break; } } // Draw label if( !label.empty() ) { LLWString::trim(label); S32 x; switch( mHAlign ) { case LLFontGL::RIGHT: x = text_right; break; case LLFontGL::HCENTER: x = mRect.getWidth() / 2; break; case LLFontGL::LEFT: default: x = text_left; break; } S32 y_offset = 2 + (mRect.getHeight() - 20)/2; if (pressed) { y_offset--; x++; } mGLFont->render(label, 0, (F32)x, (F32)(LLBUTTON_V_PAD + y_offset), label_color, mHAlign, LLFontGL::BOTTOM, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NORMAL, U32_MAX, text_width, NULL, FALSE, FALSE); } if (sDebugRects || (LLView::sEditingUI && this == LLView::sEditingUIView)) { drawDebugRect(); } } // reset hover status for next frame mNeedsHighlight = FALSE; }
// <dogmode> void LLChatBar::sendChat( EChatType type ) { if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if (!text.empty()) { // store sent line in history, duplicates will get filtered if (mInputEditor) mInputEditor->updateHistory(); S32 channel = 0; stripChannelNumber(text, &channel); std::string utf8text = wstring_to_utf8str(text);//+" and read is "+llformat("%f",readChan)+" and undone is "+llformat("%d",undoneChan)+" but actualy channel is "+llformat("%d",channel); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { if (gSavedSettings.getBOOL("AscentAutoCloseOOC") && (utf8text.length() > 1)) { // Chalice - OOC autoclosing patch based on code by Henri Beauchamp int needsClosingType=0; //Check if it needs the end-of-chat brackets -HgB if (utf8text.find("((") == 0 && utf8text.find("))") == -1) { if(utf8text.at(utf8text.length() - 1) == ')') utf8text+=" "; utf8text+="))"; } else if(utf8text.find("[[") == 0 && utf8text.find("]]") == -1) { if(utf8text.at(utf8text.length() - 1) == ']') utf8text+=" "; utf8text+="]]"; } //Check if it needs the start-of-chat brackets -HgB needsClosingType=0; if (utf8text.find("((") == -1 && utf8text.find("))") == (utf8text.length() - 2)) { if(utf8text.at(0) == '(') utf8text.insert(0," "); utf8text.insert(0,"(("); } else if (utf8text.find("[[") == -1 && utf8text.find("]]") == (utf8text.length() - 2)) { if(utf8text.at(0) == '[') utf8text.insert(0," "); utf8text.insert(0,"[["); } } // Convert MU*s style poses into IRC emotes here. if (gSavedSettings.getBOOL("AscentAllowMUpose") && utf8text.find(":") == 0 && utf8text.length() > 3) { if (utf8text.find(":'") == 0) { utf8text.replace(0, 1, "/me"); } else if (isalpha(utf8text.at(1))) // Do not prevent smileys and such. { utf8text.replace(0, 1, "/me "); } } // discard returned "found" boolean LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); EChatType nType; if(type == CHAT_TYPE_OOC) nType=CHAT_TYPE_NORMAL; else nType=type; if (!utf8_revised_text.empty() && cmd_line_chat(utf8_revised_text, nType)) { // Chat with animation #if SHY_MOD //Command handler if(!SHCommandHandler::handleCommand(true, utf8_revised_text, gAgentID, (LLViewerObject*)gAgentAvatarp))//returns true if handled #endif //shy_mod sendChatFromViewer(utf8_revised_text, nType, TRUE); } } } childSetValue("Chat Editor", LLStringUtil::null); gAgent.stopTyping(); // If the user wants to stop chatting on hitting return, lose focus // and go out of chat mode. if (gChatBar == this && gSavedSettings.getBOOL("CloseChatOnReturn")) { stopChat(); } }
void FSFloaterNearbyChat::sendChat( EChatType type ) { if (mInputEditor) { LLWString text = mInputEditor->getWText(); LLWStringUtil::trim(text); LLWStringUtil::replaceChar(text,182,'\n'); // Convert paragraph symbols back into newlines. if (!text.empty()) { if(type == CHAT_TYPE_OOC) { std::string tempText = wstring_to_utf8str( text ); tempText = gSavedSettings.getString("FSOOCPrefix") + " " + tempText + " " + gSavedSettings.getString("FSOOCPostfix"); text = utf8str_to_wstring(tempText); } // Check if this is destined for another channel S32 channel = 0; stripChannelNumber(text, &channel); // If "/<number>" is not specified, see if a channel has been set in // the spinner. if (gSavedSettings.getBOOL("FSNearbyChatbar") && gSavedSettings.getBOOL("FSShowChatChannel") && (channel == 0)) { channel = (S32)(FSFloaterNearbyChat::getInstance()->getChild<LLSpinCtrl>("ChatChannel")->get()); } std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { // Convert OOC and MU* style poses utf8text = applyAutoCloseOoc(utf8text); utf8text = applyMuPose(utf8text); // discard returned "found" boolean LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); EChatType nType; if(type == CHAT_TYPE_OOC) nType = CHAT_TYPE_NORMAL; else nType = type; type = processChatTypeTriggers(nType, utf8_revised_text); if (!utf8_revised_text.empty() && cmd_line_chat(utf8_revised_text, type)) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, gSavedSettings.getBOOL("PlayChatAnim")); } } mInputEditor->setText(LLStringExplicit("")); } gAgent.stopTyping(); // If the user wants to stop chatting on hitting return, lose focus // and go out of chat mode. if (gSavedSettings.getBOOL("CloseChatOnReturn") && gSavedSettings.getBOOL("FSUnfocusChatHistoryOnReturn")) { stopChat(); } }
S32 LLFontGL::render(const LLWString &wstr, S32 begin_offset, F32 x, F32 y, const LLColor4 &color, HAlign halign, VAlign valign, U8 style, ShadowType shadow, S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_ellipses) const { LLFastTimer _(FTM_RENDER_FONTS); if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty()) { return 0; } gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // determine which style flags need to be added programmatically by stripping off the // style bits that are drawn by the underlying Freetype font U8 style_to_add = (style | mFontDescriptor.getStyle()) & ~mFontFreetype->getStyle(); F32 drop_shadow_strength = 0.f; if (shadow != NO_SHADOW) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { shadow = NO_SHADOW; } } gGL.pushUIMatrix(); gGL.loadUIIdentity(); //gGL.translateUI(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); // this code snaps the text origin to a pixel grid to start with //F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); //F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); //gGL.translateUI(-pixel_offset_x, -pixel_offset_y, 0.f); LLVector2 origin(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY)); // snap the text origin to a pixel grid to start with origin.mV[VX] -= llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); origin.mV[VY] -= llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX) + origin.mV[VX]; cur_y = ((F32)y * sScaleY) + origin.mV[VY]; // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mFontFreetype->getAscenderHeight(); break; case BOTTOM: cur_y += mFontFreetype->getDescenderHeight(); break; case VCENTER: cur_y -= (mFontFreetype->getAscenderHeight() - mFontFreetype->getDescenderHeight()) / 2.f; break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), begin_offset, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = llround(cur_x); const LLFontBitmapCache* font_bitmap_cache = mFontFreetype->getFontBitmapCache(); F32 inv_width = 1.f / font_bitmap_cache->getBitmapWidth(); F32 inv_height = 1.f / font_bitmap_cache->getBitmapHeight(); const S32 LAST_CHARACTER = LLFontFreetype::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses) { // check for too long of a string S32 string_width = llround(getWidthF32(wstr.c_str(), begin_offset, max_chars) * sScaleX); if (string_width > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } const LLFontGlyphInfo* next_glyph = NULL; const S32 GLYPH_BATCH_SIZE = 30; LLVector3 vertices[GLYPH_BATCH_SIZE * 4]; LLVector2 uvs[GLYPH_BATCH_SIZE * 4]; LLColor4U colors[GLYPH_BATCH_SIZE * 4]; LLColor4U text_color(color); S32 bitmap_num = -1; S32 glyph_count = 0; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; const LLFontGlyphInfo* fgi = next_glyph; next_glyph = NULL; if(!fgi) { fgi = mFontFreetype->getGlyphInfo(wch); } if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } // Per-glyph bitmap texture. S32 next_bitmap_num = fgi->mBitmapNum; if (next_bitmap_num != bitmap_num) { bitmap_num = next_bitmap_num; LLImageGL *font_image = font_bitmap_cache->getImageGL(bitmap_num); gGL.getTexUnit(0)->bind(font_image); } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), llround(cur_render_y + (F32)fgi->mYBearing), llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); if (glyph_count >= GLYPH_BATCH_SIZE) { gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); glyph_count = 0; } drawGlyph(glyph_count, vertices, uvs, colors, screen_rect, uv_rect, text_color, style_to_add, shadow, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. next_glyph = mFontFreetype->getGlyphInfo(next_char); cur_x += mFontFreetype->getXKerning(fgi, next_glyph); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llround(cur_x); //cur_y = (F32)llround(cur_y); cur_render_x = cur_x; cur_render_y = cur_y; } gGL.begin(LLRender::QUADS); { gGL.vertexBatchPreTransformed(vertices, uvs, colors, glyph_count * 4); } gGL.end(); if (right_x) { *right_x = (cur_x - origin.mV[VX]) / sScaleX; } //FIXME: add underline as glyph? if (style_to_add & UNDERLINE) { F32 descender = mFontFreetype->getDescenderHeight(); gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - (descender)); gGL.vertex2f(cur_x, cur_y - (descender)); gGL.end(); } if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushUIMatrix(); renderUTF8(std::string("..."), 0, (cur_x - origin.mV[VX]) / sScaleX, (F32)y, color, LEFT, valign, style_to_add, shadow, S32_MAX, max_pixels, right_x, FALSE); gGL.popUIMatrix(); } gGL.popUIMatrix(); return chars_drawn; }
void LLNearbyChat::sendChat(LLWString text,EChatType type) { LLWStringUtil::trim(text); if (!text.empty()) { if(type == CHAT_TYPE_OOC) { std::string tempText = wstring_to_utf8str( text ); tempText = gSavedSettings.getString("FSOOCPrefix") + " " + tempText + " " + gSavedSettings.getString("FSOOCPostfix"); text = utf8str_to_wstring(tempText); } // Check if this is destined for another channel S32 channel = 0; stripChannelNumber(text, &channel); // If "/<number>" is not specified, see if a channel has been set in // the spinner. if (gSavedSettings.getBOOL("FSNearbyChatbar") && gSavedSettings.getBOOL("FSShowChatChannel") && (channel == 0)) { channel = (S32)(LLFloaterNearbyChat::getInstance()->getChild<LLSpinCtrl>("ChatChannel")->get()); } std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { //-TT Satomi Ahn - Patch MU_OOC if (gSavedSettings.getBOOL("AutoCloseOOC")) { // Try to find any unclosed OOC chat (i.e. an opening // double parenthesis without a matching closing double // parenthesis. if (utf8text.find("(( ") != -1 && utf8text.find("))") == -1) { // add the missing closing double parenthesis. utf8text += " ))"; } else if (utf8text.find("((") != -1 && utf8text.find("))") == -1) { if (utf8text.at(utf8text.length() - 1) == ')') { // cosmetic: add a space first to avoid a closing triple parenthesis utf8text += " "; } // add the missing closing double parenthesis. utf8text += "))"; } else if (utf8text.find("[[ ") != -1 && utf8text.find("]]") == -1) { // add the missing closing double parenthesis. utf8text += " ]]"; } else if (utf8text.find("[[") != -1 && utf8text.find("]]") == -1) { if (utf8text.at(utf8text.length() - 1) == ']') { // cosmetic: add a space first to avoid a closing triple parenthesis utf8text += " "; } // add the missing closing double parenthesis. utf8text += "]]"; } } // Convert MU*s style poses into IRC emotes here. if (gSavedSettings.getBOOL("AllowMUpose") && utf8text.find(":") == 0 && utf8text.length() > 3) { if (utf8text.find(":'") == 0) { utf8text.replace(0, 1, "/me"); } else if (!isdigit(utf8text.at(1)) && !ispunct(utf8text.at(1)) && !isspace(utf8text.at(1))) // Do not prevent smileys and such. { utf8text.replace(0, 1, "/me "); } } //-TT Satomi Ahn - Patch MU_OOC // discard returned "found" boolean LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); EChatType nType; if(type == CHAT_TYPE_OOC) nType = CHAT_TYPE_NORMAL; else nType = type; type = processChatTypeTriggers(nType, utf8_revised_text); if (!utf8_revised_text.empty() && cmd_line_chat(utf8_revised_text, type)) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, gSavedSettings.getBOOL("FSPlayChatAnimation")); } } gAgent.stopTyping(); }
// Walk through a string, applying the rules specified by the keyword token list and // create a list of color segments. void LLKeywords::findSegments(std::vector<LLTextSegment *>* seg_list, const LLWString& wtext, const LLColor4 &defaultColor) { std::for_each(seg_list->begin(), seg_list->end(), DeletePointer()); seg_list->clear(); if( wtext.empty() ) { return; } S32 text_len = wtext.size(); seg_list->push_back( new LLTextSegment( LLColor3(defaultColor), 0, text_len ) ); const llwchar* base = wtext.c_str(); const llwchar* cur = base; const llwchar* line = NULL; while( *cur ) { if( *cur == '\n' || cur == base ) { if( *cur == '\n' ) { cur++; if( !*cur || *cur == '\n' ) { continue; } } // Start of a new line line = cur; // Skip white space while( *cur && isspace(*cur) && (*cur != '\n') ) { cur++; } if( !*cur || *cur == '\n' ) { continue; } // cur is now at the first non-whitespace character of a new line // Line start tokens { BOOL line_done = FALSE; for (token_list_t::iterator iter = mLineTokenList.begin(); iter != mLineTokenList.end(); ++iter) { LLKeywordToken* cur_token = *iter; if( cur_token->isHead( cur ) ) { S32 seg_start = cur - base; while( *cur && *cur != '\n' ) { // skip the rest of the line cur++; } S32 seg_end = cur - base; LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end ); text_segment->setToken( cur_token ); insertSegment( seg_list, text_segment, text_len, defaultColor); line_done = TRUE; // to break out of second loop. break; } } if( line_done ) { continue; } } } // Skip white space while( *cur && isspace(*cur) && (*cur != '\n') ) { cur++; } while( *cur && *cur != '\n' ) { // Check against delimiters { S32 seg_start = 0; LLKeywordToken* cur_delimiter = NULL; for (token_list_t::iterator iter = mDelimiterTokenList.begin(); iter != mDelimiterTokenList.end(); ++iter) { LLKeywordToken* delimiter = *iter; if( delimiter->isHead( cur ) ) { cur_delimiter = delimiter; break; } } if( cur_delimiter ) { S32 between_delimiters = 0; S32 seg_end = 0; seg_start = cur - base; cur += cur_delimiter->getLength(); if( cur_delimiter->getType() == LLKeywordToken::TWO_SIDED_DELIMITER ) { while( *cur && !cur_delimiter->isHead(cur)) { // Check for an escape sequence. if (*cur == '\\') { // Count the number of backslashes. S32 num_backslashes = 0; while (*cur == '\\') { num_backslashes++; between_delimiters++; cur++; } // Is the next character the end delimiter? if (cur_delimiter->isHead(cur)) { // Is there was an odd number of backslashes, then this delimiter // does not end the sequence. if (num_backslashes % 2 == 1) { between_delimiters++; cur++; } else { // This is an end delimiter. break; } } } else { between_delimiters++; cur++; } } if( *cur ) { cur += cur_delimiter->getLength(); seg_end = seg_start + between_delimiters + 2 * cur_delimiter->getLength(); } else { // eof seg_end = seg_start + between_delimiters + cur_delimiter->getLength(); } } else { llassert( cur_delimiter->getType() == LLKeywordToken::ONE_SIDED_DELIMITER ); // Left side is the delimiter. Right side is eol or eof. while( *cur && ('\n' != *cur) ) { between_delimiters++; cur++; } seg_end = seg_start + between_delimiters + cur_delimiter->getLength(); } LLTextSegment* text_segment = new LLTextSegment( cur_delimiter->getColor(), seg_start, seg_end ); text_segment->setToken( cur_delimiter ); insertSegment( seg_list, text_segment, text_len, defaultColor); // Note: we don't increment cur, since the end of one delimited seg may be immediately // followed by the start of another one. continue; } } // check against words llwchar prev = cur > base ? *(cur-1) : 0; if( !isalnum( prev ) && (prev != '_') ) { const llwchar* p = cur; while( isalnum( *p ) || (*p == '_') ) { p++; } S32 seg_len = p - cur; if( seg_len > 0 ) { LLWString word( cur, 0, seg_len ); word_token_map_t::iterator map_iter = mWordTokenMap.find(word); if( map_iter != mWordTokenMap.end() ) { LLKeywordToken* cur_token = map_iter->second; S32 seg_start = cur - base; S32 seg_end = seg_start + seg_len; // llinfos << "Seg: [" << word.c_str() << "]" << llendl; LLTextSegment* text_segment = new LLTextSegment( cur_token->getColor(), seg_start, seg_end ); text_segment->setToken( cur_token ); insertSegment( seg_list, text_segment, text_len, defaultColor); } cur += seg_len; continue; } } if( *cur && *cur != '\n' ) { cur++; } } } }
S32 LLFontGL::render(const LLWString &wstr, const S32 begin_offset, const F32 x, const F32 y, const LLColor4 &color, const HAlign halign, const VAlign valign, U8 style, const S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { if(!sDisplayFont) //do not display texts { return wstr.length() ; } LLGLEnable tex(GL_TEXTURE_2D); if (wstr.empty()) { return 0; } S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // HACK for better bolding if (style & BOLD) { if (this == LLFontGL::sSansSerif) { return LLFontGL::sSansSerifBold->render( wstr, begin_offset, x, y, color, halign, valign, (style & ~BOLD), max_chars, max_pixels, right_x, use_embedded); } } F32 drop_shadow_strength = 0.f; if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT); } } gGL.pushMatrix(); glLoadIdentity(); gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); //glScalef(sScaleX, sScaleY, 1.0f); // avoid half pixels // RN: if we're going to this trouble, might as well snap to nearest pixel all the time // but the plan is to get rid of this so that fonts "just work" //F32 half_pixel_distance = llabs(fmodf(sCurOrigin.mX * sScaleX, 1.f) - 0.5f); //if (half_pixel_distance < PIXEL_BORDER_THRESHOLD) //{ gGL.translatef(PIXEL_CORRECTION_DISTANCE*sScaleX, 0.f, 0.f); //} // this code would just snap to pixel grid, although it seems to introduce more jitter //F32 pixel_offset_x = llround(sCurOrigin.mX * sScaleX) - (sCurOrigin.mX * sScaleX); //F32 pixel_offset_y = llround(sCurOrigin.mY * sScaleY) - (sCurOrigin.mY * sScaleY); //gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); // scale back to native pixel size //glScalef(1.f / sScaleX, 1.f / sScaleY, 1.f); //glScaled(1.0 / (F64) sScaleX, 1.0 / (F64) sScaleY, 1.0f); LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); gGL.color4fv( color.mV ); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Bind the font texture mImageGLp->bind(0); // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX); cur_y = ((F32)y * sScaleY); // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mAscender; break; case BOTTOM: cur_y += mDescender; break; case VCENTER: cur_y -= ((mAscender - mDescender)/2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; break; default: break; } // Round properly. //cur_render_y = (F32)llfloor(cur_y/sScaleY + 0.5f)*sScaleY; //cur_render_x = (F32)llfloor(cur_x/sScaleX + 0.5f)*sScaleX; cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = cur_x; F32 inv_width = 1.f / mImageGLp->getWidth(); F32 inv_height = 1.f / mImageGLp->getHeight(); const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + gExtCharFont->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } ext_image->bind(); const F32 ext_x = cur_render_x + (EXT_X_BEARING * sScaleX); const F32 ext_y = cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength); if (!label.empty()) { gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); gExtCharFont->render(label, 0, /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), /*llfloor*/(cur_y / sScaleY), color, halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } gGL.color4fv(color.mV); chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; // Bind the font texture mImageGLp->bind(); } else { if (!hasGlyph(wch)) { (const_cast<LLFontGL*>(this))->addChar(wch); } const LLFontGlyphInfo* fgi= getGlyphInfo(wch); if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset - PAD_AMT) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_AMT) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth + PAD_AMT) * inv_width, (fgi->mYBitmapOffset - PAD_AMT) * inv_height); LLRectf screen_rect(cur_render_x + (F32)fgi->mXBearing - PAD_AMT, cur_render_y + (F32)fgi->mYBearing + PAD_AMT, cur_render_x + (F32)fgi->mXBearing + (F32)fgi->mWidth + PAD_AMT, cur_render_y + (F32)fgi->mYBearing - (F32)fgi->mHeight - PAD_AMT); drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. if (!hasGlyph(next_char)) { (const_cast<LLFontGL*>(this))->addChar(next_char); } cur_x += getXKerning(wch, next_char); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llfloor(cur_x + 0.5f); //cur_y = (F32)llfloor(cur_y + 0.5f); cur_render_x = cur_x; cur_render_y = cur_y; } } if (right_x) { *right_x = cur_x / sScaleX; } if (style & UNDERLINE) { LLGLSNoTexture no_texture; gGL.begin(LLVertexBuffer::LINES); gGL.vertex2f(start_x, cur_y - (mDescender)); gGL.vertex2f(cur_x, cur_y - (mDescender)); gGL.end(); } // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); renderUTF8(std::string("..."), 0, cur_x / sScaleX, (F32)y, color, LEFT, valign, style, S32_MAX, max_pixels, right_x, FALSE); gGL.popMatrix(); } gGL.popMatrix(); return chars_drawn; }
// virtual void LLButton::draw() { BOOL flash = FALSE; if( mFlashing ) { F32 elapsed = mFlashingTimer.getElapsedTimeF32(); S32 flash_count = S32(elapsed * LLUI::sConfigGroup->getF32("ButtonFlashRate") * 2.f); // flash on or off? flash = (flash_count % 2 == 0) || flash_count > S32((F32)LLUI::sConfigGroup->getS32("ButtonFlashCount") * 2.f); } BOOL pressed_by_keyboard = FALSE; if (hasFocus()) { pressed_by_keyboard = gKeyboard->getKeyDown(' ') || (mCommitOnReturn && gKeyboard->getKeyDown(KEY_RETURN)); } // Unselected image assignments S32 local_mouse_x; S32 local_mouse_y; LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y); BOOL pressed = pressed_by_keyboard || (hasMouseCapture() && pointInView(local_mouse_x, local_mouse_y)) || mToggleState; BOOL use_glow_effect = FALSE; LLColor4 glow_color = LLColor4::white; LLRender::eBlendType glow_type = LLRender::BT_ADD_WITH_ALPHA; if ( mNeedsHighlight ) { if (pressed) { if (mImageHoverSelected) { mImagep = mImageHoverSelected; } else { mImagep = mImageSelected; use_glow_effect = TRUE; } } else { if (mImageHoverUnselected) { mImagep = mImageHoverUnselected; } else { mImagep = mImageUnselected; use_glow_effect = TRUE; } } } else if ( pressed ) { mImagep = mImageSelected; } else { mImagep = mImageUnselected; } if (mFlashing) { use_glow_effect = TRUE; glow_type = LLRender::BT_ALPHA; // blend the glow if (mNeedsHighlight) // highlighted AND flashing glow_color = (glow_color*0.5f + mFlashBgColor*0.5f) % 2.0f; // average between flash and highlight colour, with sum of the opacity else glow_color = mFlashBgColor; } // Override if more data is available // HACK: Use gray checked state to mean either: // enabled and tentative // or // disabled but checked if (!mImageDisabledSelected.isNull() && ( (getEnabled() && getTentative()) || (!getEnabled() && pressed ) ) ) { mImagep = mImageDisabledSelected; } else if (!mImageDisabled.isNull() && !getEnabled() && !pressed) { mImagep = mImageDisabled; } if (mNeedsHighlight && !mImagep) { use_glow_effect = TRUE; } // Figure out appropriate color for the text LLColor4 label_color; // label changes when button state changes, not when pressed if ( getEnabled() ) { if ( mToggleState ) { label_color = mSelectedLabelColor; } else { label_color = mUnselectedLabelColor; } } else { if ( mToggleState ) { label_color = mDisabledSelectedLabelColor; } else { label_color = mDisabledLabelColor; } } // Unselected label assignments LLWString label; if( mToggleState ) { if( getEnabled() || mDisabledSelectedLabel.empty() ) { label = mSelectedLabel; } else { label = mDisabledSelectedLabel; } } else { if( getEnabled() || mDisabledLabel.empty() ) { label = mUnselectedLabel; } else { label = mDisabledLabel; } } // overlay with keyboard focus border if (hasFocus()) { F32 lerp_amt = gFocusMgr.getFocusFlashAmt(); drawBorder(gFocusMgr.getFocusColor(), llround(lerp(1.f, 3.f, lerp_amt))); } if (use_glow_effect) { mCurGlowStrength = lerp(mCurGlowStrength, mFlashing ? (flash? 1.0 : 0.0) : mHoverGlowStrength, LLCriticalDamp::getInterpolant(0.05f)); } else { mCurGlowStrength = lerp(mCurGlowStrength, 0.f, LLCriticalDamp::getInterpolant(0.05f)); } // Draw button image, if available. // Otherwise draw basic rectangular button. if (mImagep.notNull()) { if ( mScaleImage) { mImagep->draw(getLocalRect(), getEnabled() ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); mImagep->drawSolid(0, 0, getRect().getWidth(), getRect().getHeight(), glow_color % mCurGlowStrength); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } else { mImagep->draw(0, 0, getEnabled() ? mImageColor : mDisabledImageColor ); if (mCurGlowStrength > 0.01f) { gGL.setSceneBlendType(glow_type); mImagep->drawSolid(0, 0, glow_color % mCurGlowStrength); gGL.setSceneBlendType(LLRender::BT_ALPHA); } } } else { // no image llwarns << "No image for button " << getName() << llendl; // draw it in pink so we can find it gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0, LLColor4::pink1, FALSE); } // let overlay image and text play well together S32 text_left = mLeftHPad; S32 text_right = getRect().getWidth() - mRightHPad; S32 text_width = getRect().getWidth() - mLeftHPad - mRightHPad; S32 text_middle = text_left + text_width/2; // draw overlay image if (mImageOverlay.notNull() && mImageOverlay->getWidth() > 1) { // get max width and height (discard level 0) S32 overlay_width = mImageOverlay->getWidth(); S32 overlay_height = mImageOverlay->getHeight(); F32 scale_factor = llmin((F32)getRect().getWidth() / (F32)overlay_width, (F32)getRect().getHeight() / (F32)overlay_height, 1.f); overlay_width = llround((F32)overlay_width * scale_factor); overlay_height = llround((F32)overlay_height * scale_factor); S32 center_x = getLocalRect().getCenterX(); S32 center_y = getLocalRect().getCenterY(); //FUGLY HACK FOR "DEPRESSED" BUTTONS if (pressed) { center_y--; center_x++; } // fade out overlay images on disabled buttons LLColor4 overlay_color = mImageOverlayColor; if (!getEnabled()) { overlay_color.mV[VALPHA] = 0.5f; } switch(mImageOverlayAlignment) { case LLFontGL::LEFT: text_left += overlay_width + 1; text_width -= overlay_width + 1; text_middle += (overlay_width+1)/4; mImageOverlay->draw( mLeftHPad, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::HCENTER: mImageOverlay->draw( center_x - (overlay_width / 2), center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; case LLFontGL::RIGHT: text_right -= overlay_width + 1; text_width -= overlay_width + 1; text_middle += (overlay_width+1)/4; mImageOverlay->draw( getRect().getWidth() - mRightHPad - overlay_width, center_y - (overlay_height / 2), overlay_width, overlay_height, overlay_color); break; default: // draw nothing break; } } // Draw label if( !label.empty() ) { LLWStringUtil::trim(label); S32 x; switch( mHAlign ) { case LLFontGL::RIGHT: x = text_right; break; case LLFontGL::HCENTER: { S32 actual_width = mGLFont->getWidth(label.c_str()); x = llmax(text_middle, text_left + actual_width/2); } break; case LLFontGL::LEFT: default: x = text_left; break; } S32 y_offset = 2 + (getRect().getHeight() - 20)/2; if (pressed) { y_offset--; x++; } mGLFont->render(label, 0, (F32)x, (F32)(LLBUTTON_V_PAD + y_offset), label_color, mHAlign, LLFontGL::BOTTOM, LLFontGL::NORMAL, mDropShadowedText ? LLFontGL::DROP_SHADOW_SOFT : LLFontGL::NO_SHADOW, U32_MAX, text_width, NULL, FALSE, FALSE); } if (sDebugRects || (LLView::sEditingUI && this == LLView::sEditingUIView)) { drawDebugRect(); } // reset hover status for next frame mNeedsHighlight = FALSE; }
void LLNearbyChatBar::sendChat( EChatType type ) { if (mChatBox) { LLWString text = mChatBox->getConvertedText(); if (!text.empty()) { if(type == CHAT_TYPE_OOC) { std::string tempText = mChatBox->getText(); tempText = gSavedSettings.getString("PhoenixOOCPrefix") + " " + tempText + " " + gSavedSettings.getString("PhoenixOOCPostfix"); mChatBox->setText(tempText); text = utf8str_to_wstring(tempText); } // store sent line in history, duplicates will get filtered mChatBox->updateHistory(); // Check if this is destined for another channel S32 channel = 0; stripChannelNumber(text, &channel); std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { //-TT Satomi Ahn - Patch MU_OOC if (gSavedSettings.getBOOL("AutoCloseOOC")) { // Try to find any unclosed OOC chat (i.e. an opening // double parenthesis without a matching closing double // parenthesis. if (utf8text.find("(( ") != -1 && utf8text.find("))") == -1) { // add the missing closing double parenthesis. utf8text += " ))"; } else if (utf8text.find("((") != -1 && utf8text.find("))") == -1) { if (utf8text.at(utf8text.length() - 1) == ')') { // cosmetic: add a space first to avoid a closing triple parenthesis utf8text += " "; } // add the missing closing double parenthesis. utf8text += "))"; } else if (utf8text.find("[[ ") != -1 && utf8text.find("]]") == -1) { // add the missing closing double parenthesis. utf8text += " ]]"; } else if (utf8text.find("[[") != -1 && utf8text.find("]]") == -1) { if (utf8text.at(utf8text.length() - 1) == ']') { // cosmetic: add a space first to avoid a closing triple parenthesis utf8text += " "; } // add the missing closing double parenthesis. utf8text += "]]"; } } // Convert MU*s style poses into IRC emotes here. if (gSavedSettings.getBOOL("AllowMUpose") && utf8text.find(":") == 0 && utf8text.length() > 3) { if (utf8text.find(":'") == 0) { utf8text.replace(0, 1, "/me"); } else if (isalpha(utf8text.at(1))) // Do not prevent smileys and such. { utf8text.replace(0, 1, "/me "); } } //-TT Satomi Ahn - Patch MU_OOC // discard returned "found" boolean LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text); } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); EChatType nType; if(type == CHAT_TYPE_OOC) nType = CHAT_TYPE_NORMAL; else nType = type; type = processChatTypeTriggers(nType, utf8_revised_text); if (!utf8_revised_text.empty() && cmd_line_chat(utf8_revised_text, type)) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, PhoenixPlayChatAnimation); } } mChatBox->setText(LLStringExplicit("")); } gAgent.stopTyping(); // If the user wants to stop chatting on hitting return, lose focus // and go out of chat mode. if (gSavedSettings.getBOOL("CloseChatOnReturn")) { stopChat(); } }
void hud_render_text(const LLWString &wstr, const LLVector3 &pos_agent, const LLFontGL &font, const U8 style, const F32 x_offset, const F32 y_offset, const LLColor4& color, const BOOL orthographic) { LLViewerCamera* camera = LLViewerCamera::getInstance(); // Do cheap plane culling LLVector3 dir_vec = pos_agent - camera->getOrigin(); dir_vec /= dir_vec.magVec(); if (wstr.empty() || (!orthographic && dir_vec * camera->getAtAxis() <= 0.f)) { return; } LLVector3 right_axis; LLVector3 up_axis; if (orthographic) { right_axis.setVec(0.f, -1.f / gViewerWindow->getWindowHeight(), 0.f); up_axis.setVec(0.f, 0.f, 1.f / gViewerWindow->getWindowHeight()); } else { camera->getPixelVectors(pos_agent, up_axis, right_axis); } LLCoordFrame render_frame = *camera; LLQuaternion rot; if (!orthographic) { rot = render_frame.getQuaternion(); rot = rot * LLQuaternion(-F_PI_BY_TWO, camera->getYAxis()); rot = rot * LLQuaternion(F_PI_BY_TWO, camera->getXAxis()); } else { rot = LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 0.f, 1.f)); rot = rot * LLQuaternion(-F_PI_BY_TWO, LLVector3(0.f, 1.f, 0.f)); } F32 angle; LLVector3 axis; rot.getAngleAxis(&angle, axis); LLVector3 render_pos = pos_agent + (floorf(x_offset) * right_axis) + (floorf(y_offset) * up_axis); //get the render_pos in screen space F64 winX, winY, winZ; gluProject(render_pos.mV[0], render_pos.mV[1], render_pos.mV[2], gGLModelView, gGLProjection, (GLint*) gGLViewport, &winX, &winY, &winZ); //fonts all render orthographically, set up projection glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); LLUI::pushMatrix(); gViewerWindow->setup2DRender(); LLUI::loadIdentity(); LLUI::translate((F32) winX*1.0f/LLFontGL::sScaleX, (F32) winY*1.0f/(LLFontGL::sScaleY), -(((F32) winZ*2.f)-1.f)); //glRotatef(angle * RAD_TO_DEG, axis.mV[VX], axis.mV[VY], axis.mV[VZ]); //glScalef(right_scale, up_scale, 1.f); F32 right_x; font.render(wstr, 0, 0, 0, color, LLFontGL::LEFT, LLFontGL::BASELINE, style, wstr.length(), 1000, &right_x); LLUI::popMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); }
void FSNearbyChat::sendChat(LLWString text, EChatType type) { LLWStringUtil::trim(text); if (!text.empty()) { if (type == CHAT_TYPE_OOC) { std::string tempText = wstring_to_utf8str( text ); tempText = gSavedSettings.getString("FSOOCPrefix") + " " + tempText + " " + gSavedSettings.getString("FSOOCPostfix"); text = utf8str_to_wstring(tempText); } // Check if this is destined for another channel S32 channel = 0; bool is_set = false; stripChannelNumber(text, &channel, &sLastSpecialChatChannel, &is_set); // If "/<number>" is not specified, see if a channel has been set in // the spinner. if (!is_set && gSavedSettings.getBOOL("FSNearbyChatbar") && gSavedSettings.getBOOL("FSShowChatChannel")) { channel = (S32)(FSFloaterNearbyChat::getInstance()->getChild<LLSpinCtrl>("ChatChannel")->get()); } std::string utf8text = wstring_to_utf8str(text); // Try to trigger a gesture, if not chat to a script. std::string utf8_revised_text; if (0 == channel) { // Convert OOC and MU* style poses utf8text = applyAutoCloseOoc(utf8text); utf8text = applyMuPose(utf8text); // discard returned "found" boolean if(!LLGestureMgr::instance().triggerAndReviseString(utf8text, &utf8_revised_text)) { utf8_revised_text = utf8text; } } else { utf8_revised_text = utf8text; } utf8_revised_text = utf8str_trim(utf8_revised_text); EChatType nType; if (type == CHAT_TYPE_OOC) { nType = CHAT_TYPE_NORMAL; } else { nType = type; } type = processChatTypeTriggers(nType, utf8_revised_text); if (!utf8_revised_text.empty() && cmd_line_chat(utf8_revised_text, type)) { // Chat with animation sendChatFromViewer(utf8_revised_text, type, gSavedSettings.getBOOL("PlayChatAnim")); } } gAgent.stopTyping(); }
S32 LLFontGL::render(const LLWString &wstr, const S32 begin_offset, const F32 x, const F32 y, const LLColor4 &color, const HAlign halign, const VAlign valign, U8 style, const S32 max_chars, S32 max_pixels, F32* right_x, BOOL use_embedded, BOOL use_ellipses) const { if(!sDisplayFont) //do not display texts { return wstr.length() ; } if (wstr.empty()) { return 0; } gGL.getTexUnit(0)->enable(LLTexUnit::TT_TEXTURE); S32 scaled_max_pixels = max_pixels == S32_MAX ? S32_MAX : llceil((F32)max_pixels * sScaleX); // Strip off any style bits that are already accounted for by the font. style = style & (~getFontDesc().getStyle()); F32 drop_shadow_strength = 0.f; if (style & (DROP_SHADOW | DROP_SHADOW_SOFT)) { F32 luminance; color.calcHSL(NULL, NULL, &luminance); drop_shadow_strength = clamp_rescale(luminance, 0.35f, 0.6f, 0.f, 1.f); if (luminance < 0.35f) { style = style & ~(DROP_SHADOW | DROP_SHADOW_SOFT); } } gGL.pushMatrix(); glLoadIdentity(); gGL.translatef(floorf(sCurOrigin.mX*sScaleX), floorf(sCurOrigin.mY*sScaleY), sCurOrigin.mZ); // this code snaps the text origin to a pixel grid to start with F32 pixel_offset_x = llround((F32)sCurOrigin.mX) - (sCurOrigin.mX); F32 pixel_offset_y = llround((F32)sCurOrigin.mY) - (sCurOrigin.mY); gGL.translatef(-pixel_offset_x, -pixel_offset_y, 0.f); LLFastTimer t(LLFastTimer::FTM_RENDER_FONTS); gGL.color4fv( color.mV ); S32 chars_drawn = 0; S32 i; S32 length; if (-1 == max_chars) { length = (S32)wstr.length() - begin_offset; } else { length = llmin((S32)wstr.length() - begin_offset, max_chars ); } F32 cur_x, cur_y, cur_render_x, cur_render_y; // Not guaranteed to be set correctly gGL.setSceneBlendType(LLRender::BT_ALPHA); cur_x = ((F32)x * sScaleX); cur_y = ((F32)y * sScaleY); // Offset y by vertical alignment. switch (valign) { case TOP: cur_y -= mAscender; break; case BOTTOM: cur_y += mDescender; break; case VCENTER: cur_y -= ((mAscender - mDescender)/2.f); break; case BASELINE: // Baseline, do nothing. break; default: break; } switch (halign) { case LEFT: break; case RIGHT: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)); break; case HCENTER: cur_x -= llmin(scaled_max_pixels, llround(getWidthF32(wstr.c_str(), 0, length) * sScaleX)) / 2; break; default: break; } cur_render_y = cur_y; cur_render_x = cur_x; F32 start_x = cur_x; F32 inv_width = 1.f / mFontBitmapCachep->getBitmapWidth(); F32 inv_height = 1.f / mFontBitmapCachep->getBitmapHeight(); const S32 LAST_CHARACTER = LLFont::LAST_CHAR_FULL; BOOL draw_ellipses = FALSE; if (use_ellipses && halign == LEFT) { // check for too long of a string if (getWidthF32(wstr.c_str(), 0, max_chars) * sScaleX > scaled_max_pixels) { // use four dots for ellipsis width to generate padding const LLWString dots(utf8str_to_wstring(std::string("...."))); scaled_max_pixels = llmax(0, scaled_max_pixels - llround(getWidthF32(dots.c_str()))); draw_ellipses = TRUE; } } // Remember last-used texture to avoid unnecesssary bind calls. LLImageGL *last_bound_texture = NULL; for (i = begin_offset; i < begin_offset + length; i++) { llwchar wch = wstr[i]; // Handle embedded characters first, if they're enabled. // Embedded characters are a hack for notecards const embedded_data_t* ext_data = use_embedded ? getEmbeddedCharData(wch) : NULL; if (ext_data) { LLImageGL* ext_image = ext_data->mImage; const LLWString& label = ext_data->mLabel; F32 ext_height = (F32)ext_image->getHeight() * sScaleY; F32 ext_width = (F32)ext_image->getWidth() * sScaleX; F32 ext_advance = (EXT_X_BEARING * sScaleX) + ext_width; if (!label.empty()) { ext_advance += (EXT_X_BEARING + getFontExtChar()->getWidthF32( label.c_str() )) * sScaleX; } if (start_x + scaled_max_pixels < cur_x + ext_advance) { // Not enough room for this character. break; } if (last_bound_texture != ext_image) { gGL.getTexUnit(0)->bind(ext_image); last_bound_texture = ext_image; } // snap origin to whole screen pixel const F32 ext_x = (F32)llround(cur_render_x + (EXT_X_BEARING * sScaleX)); const F32 ext_y = (F32)llround(cur_render_y + (EXT_Y_BEARING * sScaleY + mAscender - mLineHeight)); LLRectf uv_rect(0.f, 1.f, 1.f, 0.f); LLRectf screen_rect(ext_x, ext_y + ext_height, ext_x + ext_width, ext_y); drawGlyph(screen_rect, uv_rect, LLColor4::white, style, drop_shadow_strength); if (!label.empty()) { gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); getFontExtChar()->render(label, 0, /*llfloor*/((ext_x + (F32)ext_image->getWidth() + EXT_X_BEARING) / sScaleX), /*llfloor*/(cur_y / sScaleY), color, halign, BASELINE, NORMAL, S32_MAX, S32_MAX, NULL, TRUE ); gGL.popMatrix(); } gGL.color4fv(color.mV); chars_drawn++; cur_x += ext_advance; if (((i + 1) < length) && wstr[i+1]) { cur_x += EXT_KERNING * sScaleX; } cur_render_x = cur_x; } else { if (!hasGlyph(wch)) { addChar(wch); } const LLFontGlyphInfo* fgi= getGlyphInfo(wch); if (!fgi) { llerrs << "Missing Glyph Info" << llendl; break; } // Per-glyph bitmap texture. LLImageGL *image_gl = mFontBitmapCachep->getImageGL(fgi->mBitmapNum); if (last_bound_texture != image_gl) { gGL.getTexUnit(0)->bind(image_gl); last_bound_texture = image_gl; } if ((start_x + scaled_max_pixels) < (cur_x + fgi->mXBearing + fgi->mWidth)) { // Not enough room for this character. break; } // Draw the text at the appropriate location //Specify vertices and texture coordinates LLRectf uv_rect((fgi->mXBitmapOffset) * inv_width, (fgi->mYBitmapOffset + fgi->mHeight + PAD_UVY) * inv_height, (fgi->mXBitmapOffset + fgi->mWidth) * inv_width, (fgi->mYBitmapOffset - PAD_UVY) * inv_height); // snap glyph origin to whole screen pixel LLRectf screen_rect(llround(cur_render_x + (F32)fgi->mXBearing), llround(cur_render_y + (F32)fgi->mYBearing), llround(cur_render_x + (F32)fgi->mXBearing) + (F32)fgi->mWidth, llround(cur_render_y + (F32)fgi->mYBearing) - (F32)fgi->mHeight); drawGlyph(screen_rect, uv_rect, color, style, drop_shadow_strength); chars_drawn++; cur_x += fgi->mXAdvance; cur_y += fgi->mYAdvance; llwchar next_char = wstr[i+1]; if (next_char && (next_char < LAST_CHARACTER)) { // Kern this puppy. if (!hasGlyph(next_char)) { addChar(next_char); } cur_x += getXKerning(wch, next_char); } // Round after kerning. // Must do this to cur_x, not just to cur_render_x, otherwise you // will squish sub-pixel kerned characters too close together. // For example, "CCCCC" looks bad. cur_x = (F32)llfloor(cur_x + 0.5f); //cur_y = (F32)llfloor(cur_y + 0.5f); cur_render_x = cur_x; cur_render_y = cur_y; } } if (right_x) { *right_x = cur_x / sScaleX; } if (style & UNDERLINE) { gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE); gGL.begin(LLRender::LINES); gGL.vertex2f(start_x, cur_y - (mDescender)); gGL.vertex2f(cur_x, cur_y - (mDescender)); gGL.end(); } // *FIX: get this working in all alignment cases, etc. if (draw_ellipses) { // recursively render ellipses at end of string // we've already reserved enough room gGL.pushMatrix(); //glLoadIdentity(); //gGL.translatef(sCurOrigin.mX, sCurOrigin.mY, 0.0f); //glScalef(sScaleX, sScaleY, 1.f); renderUTF8(std::string("..."), 0, cur_x / sScaleX, (F32)y, color, LEFT, valign, style, S32_MAX, max_pixels, right_x, FALSE); gGL.popMatrix(); } gGL.popMatrix(); return chars_drawn; }
void LLIMFloater::sendMsg() { if (!gAgent.isGodlike() && (mDialog == IM_NOTHING_SPECIAL) && mOtherParticipantUUID.isNull()) { llinfos << "Cannot send IM to everyone unless you're a god." << llendl; return; } if (mInputEditor) { LLWString text = mInputEditor->getConvertedText(); if(!text.empty()) { // Truncate and convert to UTF8 for transport std::string utf8_text = wstring_to_utf8str(text); utf8_text = utf8str_truncate(utf8_text, MAX_MSG_BUF_SIZE - 1); // [RLVa:KB] - Checked: 2010-11-30 (RLVa-1.3.0c) | Modified: RLVa-1.3.0c if ( (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIM)) || (gRlvHandler.hasBehaviour(RLV_BHVR_SENDIMTO)) ) { LLIMModel::LLIMSession* pIMSession = LLIMModel::instance().findIMSession(mSessionID); RLV_ASSERT(pIMSession); bool fRlvFilter = !pIMSession; if (pIMSession) { switch (pIMSession->mSessionType) { case LLIMModel::LLIMSession::P2P_SESSION: // One-on-one IM fRlvFilter = !gRlvHandler.canSendIM(mOtherParticipantUUID); break; case LLIMModel::LLIMSession::GROUP_SESSION: // Group chat fRlvFilter = !gRlvHandler.canSendIM(mSessionID); break; case LLIMModel::LLIMSession::ADHOC_SESSION: // Conference chat: allow if all participants can be sent an IM { if (!pIMSession->mSpeakers) { fRlvFilter = true; break; } LLSpeakerMgr::speaker_list_t speakers; pIMSession->mSpeakers->getSpeakerList(&speakers, TRUE); for (LLSpeakerMgr::speaker_list_t::const_iterator itSpeaker = speakers.begin(); itSpeaker != speakers.end(); ++itSpeaker) { const LLSpeaker* pSpeaker = *itSpeaker; if ( (gAgent.getID() != pSpeaker->mID) && (!gRlvHandler.canSendIM(pSpeaker->mID)) ) { fRlvFilter = true; break; } } } break; default: fRlvFilter = true; break; } } if (fRlvFilter) utf8_text = RlvStrings::getString(RLV_STRING_BLOCKED_SENDIM); } // [/RLVa:KB] if (mSessionInitialized) { LLIMModel::sendMessage(utf8_text, mSessionID, mOtherParticipantUUID,mDialog); } else { //queue up the message to send once the session is initialized mQueuedMsgsForInit.append(utf8_text); } mInputEditor->setText(LLStringUtil::null); updateMessages(); } } }