lltabcontainervertical.cpp

Go to the documentation of this file.
00001 
00032 // Fear my script-fu!
00033 
00034 #include "linden_common.h"
00035 
00036 #include "lltabcontainervertical.h"
00037 
00038 #include "llfocusmgr.h"
00039 #include "llfontgl.h"
00040 #include "llgl.h"
00041 
00042 #include "llbutton.h"
00043 #include "llrect.h"
00044 #include "llpanel.h"
00045 #include "llresmgr.h"
00046 #include "llkeyboard.h"
00047 #include "llui.h"
00048 #include "lltextbox.h"
00049 #include "llcontrol.h"
00050 #include "llcriticaldamp.h"
00051 
00052 #include "llglheaders.h"
00053 
00054 LLTabContainerVertical::LLTabContainerVertical( 
00055         const LLString& name, const LLRect& rect,
00056         void(*close_callback)(void*), void* callback_userdata,
00057         U32 tab_width, BOOL bordered)
00058         : 
00059         LLTabContainerCommon(name, rect, LEFT, close_callback, callback_userdata, bordered),
00060         mTabWidth(tab_width),
00061         mUpArrowBtn(NULL),
00062         mDownArrowBtn(NULL)
00063 {
00064         initButtons();
00065 }
00066 
00067 LLTabContainerVertical::LLTabContainerVertical( 
00068         const LLString& name, const LLString& rect_control,
00069         void(*close_callback)(void*), void* callback_userdata,
00070         U32 tab_width, BOOL bordered)
00071         : 
00072         LLTabContainerCommon(name, rect_control, LEFT, close_callback, callback_userdata, bordered),
00073         mTabWidth(tab_width)
00074 {
00075         initButtons();
00076 }
00077 
00078 // Called from all constructors
00079 void LLTabContainerVertical::initButtons()
00080 {
00081         // Hack:
00082         if (mRect.getHeight() == 0 || mUpArrowBtn)
00083         {
00084                 return; // Don't have a rect yet or already got called
00085         }
00086         
00087         LLString out_id;
00088         LLString in_id;
00089 
00090         //S32 arrow_fudge = 1;          //  match new art better 
00091 
00092         // Left and right scroll arrows (for when there are too many tabs to show all at once).
00093         S32 btn_top = mRect.getHeight();
00094         S32 btn_top_lower = mRect.mBottom+TABCNTRV_ARROW_BTN_SIZE;
00095 
00096         LLRect up_arrow_btn_rect;
00097         up_arrow_btn_rect.setLeftTopAndSize( mTabWidth/2 , btn_top, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE );
00098 
00099         LLRect down_arrow_btn_rect;
00100         down_arrow_btn_rect.setLeftTopAndSize( mTabWidth/2 , btn_top_lower, TABCNTRV_ARROW_BTN_SIZE, TABCNTRV_ARROW_BTN_SIZE );
00101 
00102         out_id = "UIImgBtnScrollUpOutUUID";
00103         in_id = "UIImgBtnScrollUpInUUID";
00104         mUpArrowBtn = new LLButton(
00105                 "Up Arrow", up_arrow_btn_rect,
00106                 out_id, in_id, "",
00107                 &onPrevBtn, this, NULL );
00108         mUpArrowBtn->setHeldDownCallback(onPrevBtnHeld);
00109         mUpArrowBtn->setSaveToXML(false);
00110         mUpArrowBtn->setFollowsTop();
00111         mUpArrowBtn->setFollowsLeft();
00112         mUpArrowBtn->setTabStop(FALSE);
00113         addChild(mUpArrowBtn);
00114 
00115         out_id = "UIImgBtnScrollDownOutUUID";
00116         in_id = "UIImgBtnScrollDownInUUID";
00117         mDownArrowBtn = new LLButton(
00118                 "Down Arrow", down_arrow_btn_rect,
00119                 out_id, in_id, "",
00120                 &onNextBtn, this, NULL );
00121         mDownArrowBtn->setHeldDownCallback(onNextBtnHeld);
00122         mDownArrowBtn->setSaveToXML(false);
00123         mDownArrowBtn->setFollowsBottom();
00124         mDownArrowBtn->setFollowsLeft();
00125         mDownArrowBtn->setTabStop(FALSE);
00126         addChild(mDownArrowBtn);
00127 
00128         // set default tab group to be panel contents
00129         mDefaultTabGroup = 1;
00130 }
00131 
00132 LLTabContainerVertical::~LLTabContainerVertical()
00133 { }
00134 
00135 void LLTabContainerVertical::addTabPanel(LLPanel* child, const LLString& label,
00136                                                                                 BOOL select, 
00137                                                                                 void (*on_tab_clicked)(void*, bool), void* userdata, 
00138                                                                                 S32 indent,
00139                                                                                 BOOL placeholder, eInsertionPoint insertion_point)
00140 {
00141         if (child->getParent() == this)
00142         {
00143                 // already a child of mine
00144                 return;
00145         }
00146 
00147         const LLFontGL* font = gResMgr->getRes( LLFONT_SANSSERIF );
00148 
00149         // Store the original label for possible xml export.
00150         child->setLabel(label);
00151         // Replace long label with truncated version (e.g., "FooBa...")
00152         LLString trimmed_label = label;
00153         LLString::trim(trimmed_label);
00154 
00155         // Tab panel
00156         S32 tab_panel_top;
00157         S32 tab_panel_bottom;
00158         tab_panel_top = mRect.getHeight() 
00159                                         - mTopBorderHeight 
00160                                         - (BTN_HEIGHT - TABCNTRV_BUTTON_PANEL_OVERLAP);
00161         tab_panel_bottom = LLPANEL_BORDER_WIDTH;
00162         
00163         LLRect tab_panel_rect( 
00164                 mTabWidth + (LLPANEL_BORDER_WIDTH * 2) + TABCNTRV_PAD, 
00165                 mRect.getHeight() - LLPANEL_BORDER_WIDTH,
00166                 mRect.getWidth() - LLPANEL_BORDER_WIDTH,
00167                 LLPANEL_BORDER_WIDTH);
00168 
00169         child->setFollowsAll();
00170         child->translate( tab_panel_rect.mLeft - child->getRect().mLeft, tab_panel_rect.mBottom - child->getRect().mBottom);
00171         child->reshape( tab_panel_rect.getWidth(), tab_panel_rect.getHeight(), TRUE );
00172         child->setBackgroundVisible( FALSE );  // No need to overdraw
00173 
00174         child->setVisible( FALSE );  // Will be made visible when selected
00175 
00176         // Tab button
00177         LLRect btn_rect;
00178         btn_rect.setLeftTopAndSize(
00179                 TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2,        // JC - Fudge factor
00180                 (mRect.getHeight() - mTopBorderHeight - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * mTabList.size()),
00181                 mTabWidth,
00182                 BTN_HEIGHT);
00183 
00184         if (!placeholder)
00185         {
00186                 LLButton *btn = new LLButton("vert tab button",
00187                         btn_rect,
00188                         "tab_left.tga",
00189                         "tab_left_selected.tga", 
00190                         "", 
00191                         &LLTabContainerVertical::onTabBtn, NULL,
00192                         font,
00193                         trimmed_label, trimmed_label);
00194                 btn->setSaveToXML(false);
00195                 btn->setFixedBorder(16, 16);
00196                 btn->setScaleImage(TRUE);
00197                 btn->setHAlign(LLFontGL::LEFT);
00198                 btn->setFollows(FOLLOWS_TOP | FOLLOWS_LEFT);
00199                 btn->setTabStop(FALSE);
00200                 if (indent)
00201                 {
00202                         btn->setLeftHPad(indent);
00203                 }
00204 
00205                 LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata );
00206                 insertTuple( tuple, insertion_point ); 
00207 
00208                 btn->setCallbackUserData( tuple );
00209                 addChild( btn, 0 );
00210                 addChild(child, 1);
00211 
00212                 if( select )
00213                 {
00214                         selectTab( mTabList.size()-1 );
00215                 }
00216         }
00217         else
00218         {
00219                 btn_rect.translate(0, -LLBUTTON_V_PAD-2);
00220                 LLString box_label = trimmed_label;
00221                 LLTextBox* text = new LLTextBox(box_label, btn_rect, box_label, font);
00222                 text->setSaveToXML(false);
00223                 addChild( text, 0 );
00224 
00225                 LLButton* btn = new LLButton("", LLRect(0,0,0,0));
00226                 btn->setSaveToXML(false);
00227                 addChild(btn, 0);
00228                 addChild(child, 1);
00229 
00230                 LLTabTuple* tuple = new LLTabTuple( this, child, btn, on_tab_clicked, userdata, text );
00231                 insertTuple( tuple, insertion_point ); 
00232         }
00233 
00234         updateMaxScrollPos();
00235 }
00236 
00237 void LLTabContainerVertical::removeTabPanel(LLPanel* child)
00238 {
00239         LLTabContainerCommon::removeTabPanel(child);
00240 
00241         // Fix-up button sizes
00242         S32 tab_count = 0;
00243         for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
00244         {
00245                 LLTabTuple* tuple = *iter;
00246                 LLRect rect;
00247                 rect.setLeftTopAndSize(TABCNTRV_PAD + LLPANEL_BORDER_WIDTH + 2, // JC - Fudge factor
00248                                                            (mRect.getHeight() - LLPANEL_BORDER_WIDTH - 1) - ((BTN_HEIGHT + TABCNTRV_PAD) * (tab_count)),
00249                                                            mTabWidth,
00250                                                            BTN_HEIGHT);
00251                 if (tuple->mPlaceholderText)
00252                 {
00253                         tuple->mPlaceholderText->setRect(rect);
00254                 }
00255                 else
00256                 {
00257                         tuple->mButton->setRect(rect);
00258                 }
00259                 tab_count++;
00260         }
00261 }
00262 
00263 void LLTabContainerVertical::updateMaxScrollPos()
00264 {
00265         S32 tab_total_height = (BTN_HEIGHT + TABCNTRV_PAD) * mTabList.size();
00266         S32 available_height = mRect.getHeight() - mTopBorderHeight;
00267         if( tab_total_height > available_height )
00268         {
00269                 S32 available_height_with_arrows = mRect.getHeight() - 2*(TABCNTRV_ARROW_BTN_SIZE + 3*TABCNTRV_PAD);
00270                 S32 additional_needed = tab_total_height - available_height_with_arrows;
00271                 mMaxScrollPos = S32( ceil(additional_needed / float(BTN_HEIGHT) ) );
00272         }
00273         else
00274         {
00275                 mMaxScrollPos = 0;
00276                 mScrollPos = 0;
00277         }
00278         if (mScrollPos > mMaxScrollPos)
00279         {
00280                 mScrollPos = mMaxScrollPos;
00281         }
00282 }
00283 
00284 void LLTabContainerVertical::commitHoveredButton(S32 x, S32 y)
00285 {
00286         if (hasMouseCapture())
00287         {
00288                 for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
00289                 {
00290                         LLTabTuple* tuple = *iter;
00291                         tuple->mButton->setVisible( TRUE );
00292                         S32 local_x = x - tuple->mButton->getRect().mLeft;
00293                         S32 local_y = y - tuple->mButton->getRect().mBottom;
00294                         if (tuple->mButton->pointInView(local_x, local_y) && tuple->mButton->getEnabled() && !tuple->mTabPanel->getVisible())
00295                         {
00296                                 tuple->mButton->onCommit();
00297                         }
00298                 }
00299         }
00300 }
00301 
00302 BOOL LLTabContainerVertical::selectTab(S32 which)
00303 {
00304         if (which >= (S32)mTabList.size()) return FALSE;
00305         if (which < 0) return FALSE;
00306 
00307         //if( gFocusMgr.childHasKeyboardFocus( this ) )
00308         //{
00309         //      gFocusMgr.setKeyboardFocus( NULL, NULL );
00310         //}
00311 
00312         LLTabTuple* selected_tuple = mTabList[which];
00313         if (!selected_tuple)
00314         {
00315                 return FALSE;
00316         }
00317         
00318         BOOL is_visible = FALSE;
00319         if (which != mCurrentTabIdx)
00320         {
00321                 mCurrentTabIdx = which;
00322 
00323                 S32 i = 0;
00324                 for(tuple_list_t::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
00325                 {
00326                         LLTabTuple* tuple = *iter;
00327                         BOOL is_selected = ( tuple == selected_tuple );
00328                         tuple->mTabPanel->setVisible( is_selected );
00329 //                      tuple->mTabPanel->setFocus(is_selected); // not clear that we want to do this here.
00330                         tuple->mButton->setToggleState( is_selected );
00331                         // RN: this limits tab-stops to active button only, which would require arrow keys to switch tabs
00332                         tuple->mButton->setTabStop( is_selected );
00333                         
00334                         if( is_selected )
00335                         {
00336                                 // Make sure tab is within scroll
00337                                 S32 num_visible = mTabList.size() - mMaxScrollPos;
00338                                 if( i >= mScrollPos && i <= mScrollPos + num_visible)
00339                                 {
00340                                         mCurrentTabIdx = which;
00341                                         is_visible = TRUE;
00342                                 }
00343                                 else
00344                                 {
00345                                         is_visible = FALSE;
00346                                 }
00347                         }
00348                         i++;
00349                 }
00350                 if( selected_tuple->mOnChangeCallback )
00351                 {
00352                         selected_tuple->mOnChangeCallback( selected_tuple->mUserData, false );
00353                 }
00354         }
00355         if(mCurrentTabIdx >= 0)
00356         {
00357                 LLTabTuple* tuple = mTabList[mCurrentTabIdx];
00358                 tuple->mTabPanel->setVisible( TRUE );
00359                 tuple->mButton->setToggleState( TRUE );
00360         }
00361         return is_visible;
00362 }
00363 
00364 
00365 
00366 void LLTabContainerVertical::draw()
00367 {
00368         S32 target_pixel_scroll = mScrollPos * (BTN_HEIGHT + TABCNTRV_PAD);
00369 
00370         mScrollPosPixels = (S32)lerp((F32)mScrollPosPixels, (F32)target_pixel_scroll, LLCriticalDamp::getInterpolant(0.08f));
00371         if( getVisible() )
00372         {
00373                 BOOL has_scroll_arrows = (mMaxScrollPos > 0) || (mScrollPosPixels > 0);
00374                 mUpArrowBtn->setVisible( has_scroll_arrows );
00375                 mDownArrowBtn->setVisible( has_scroll_arrows );
00376 
00377                 // Set the topmost position of the tab buttons.
00378                 S32 top = mRect.getHeight() - mTopBorderHeight - LLPANEL_BORDER_WIDTH - 1 - (has_scroll_arrows ? TABCNTRV_ARROW_BTN_SIZE : 0);
00379                 top += mScrollPosPixels;
00380 
00381                 // Hide all the buttons
00382                 for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
00383                 {
00384                         LLTabTuple* tuple = *iter;
00385                         tuple->mButton->setVisible( FALSE );
00386                 }
00387 
00388                 LLPanel::draw();
00389 
00390                 // Show all the buttons
00391                 for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
00392                 {
00393                         LLTabTuple* tuple = *iter;
00394                         tuple->mButton->setVisible( TRUE );
00395                 }
00396 
00397                 // Draw some of the buttons...
00398                 {
00399                         LLRect clip_rect = getLocalRect();
00400                         if (has_scroll_arrows)
00401                         {
00402                                 // ...but clip them.
00403                                 clip_rect.mBottom = mDownArrowBtn->getRect().mTop + 3*TABCNTRV_PAD;
00404                                 clip_rect.mTop = mUpArrowBtn->getRect().mBottom - 3*TABCNTRV_PAD;
00405                         }
00406                         LLLocalClipRect clip(clip_rect);
00407 
00408                         //S32 max_scroll_visible = mTabList.size() - mMaxScrollPos + mScrollPos;
00409                         S32 idx = 0;
00410                         for(std::vector<LLTabTuple*>::iterator iter = mTabList.begin(); iter != mTabList.end(); ++iter)
00411                         {
00412                                 LLTabTuple* tuple = *iter;
00413                                 tuple->mButton->translate( 0 , top - tuple->mButton->getRect().mTop);
00414                                 top -= BTN_HEIGHT + TABCNTRV_PAD;
00415 
00416                                 LLUI::pushMatrix();
00417                                 {
00418                                         LLUI::translate((F32)tuple->mButton->getRect().mLeft, (F32)tuple->mButton->getRect().mBottom, 0.f);
00419                                         tuple->mButton->draw();
00420                                 }
00421                                 LLUI::popMatrix();
00422 
00423                                 idx++;
00424                         }
00425 
00426                         if( has_scroll_arrows )
00427                         {
00428                                 // Redraw the arrows so that they appears on top.
00429                                 glPushMatrix();
00430                                 glTranslatef((F32)mUpArrowBtn->getRect().mLeft, (F32)mUpArrowBtn->getRect().mBottom, 0.f);
00431                                 mUpArrowBtn->draw();
00432                                 glPopMatrix();
00433 
00434                                 glPushMatrix();
00435                                 glTranslatef((F32)mDownArrowBtn->getRect().mLeft, (F32)mDownArrowBtn->getRect().mBottom, 0.f);
00436                                 mDownArrowBtn->draw();
00437                                 glPopMatrix();
00438                         }
00439                 }
00440         }
00441 }
00442 
00443 BOOL LLTabContainerVertical::handleMouseDown( S32 x, S32 y, MASK mask )
00444 {
00445         BOOL handled = FALSE;
00446         BOOL has_scroll_arrows = (mMaxScrollPos > 0);
00447 
00448         if (has_scroll_arrows)
00449         {
00450                 if (mUpArrowBtn->getRect().pointInRect(x, y))
00451                 {
00452                         S32 local_x = x - mUpArrowBtn->getRect().mLeft;
00453                         S32 local_y = y - mUpArrowBtn->getRect().mBottom;
00454                         handled = mUpArrowBtn->handleMouseDown(local_x, local_y, mask);
00455                 }
00456                 else if (mDownArrowBtn->getRect().pointInRect(x, y))
00457                 {
00458                         S32 local_x = x - mDownArrowBtn->getRect().mLeft;
00459                         S32 local_y = y - mDownArrowBtn->getRect().mBottom;
00460                         handled = mDownArrowBtn->handleMouseDown(local_x, local_y, mask);
00461                 }
00462         }
00463         if (!handled)
00464         {
00465                 handled = LLPanel::handleMouseDown( x, y, mask );
00466         }
00467 
00468         if (mTabList.size() > 0)
00469         {
00470                 LLTabTuple* firsttuple = mTabList[0];           
00471                 LLRect tab_rect(firsttuple->mButton->getRect().mLeft,
00472                                                 has_scroll_arrows ? mUpArrowBtn->getRect().mBottom - TABCNTRV_PAD : mUpArrowBtn->getRect().mTop,
00473                                                 firsttuple->mButton->getRect().mRight,
00474                                                 has_scroll_arrows ? mDownArrowBtn->getRect().mTop + TABCNTRV_PAD : mDownArrowBtn->getRect().mBottom );
00475                 if( tab_rect.pointInRect( x, y ) )
00476                 {
00477                         LLButton* tab_button = mTabList[getCurrentPanelIndex()]->mButton;
00478                         gFocusMgr.setMouseCapture(this);
00479                         gFocusMgr.setKeyboardFocus(tab_button, NULL);
00480                 }
00481         }
00482         return handled;
00483 }
00484 
00485 BOOL LLTabContainerVertical::handleHover( S32 x, S32 y, MASK mask )
00486 {
00487         BOOL handled = FALSE;
00488         BOOL has_scroll_arrows = (mMaxScrollPos > 0);
00489 
00490         if (has_scroll_arrows)
00491         {
00492                 if (mUpArrowBtn->getRect().pointInRect(x, y))
00493                 {
00494                         S32 local_x = x - mUpArrowBtn->getRect().mLeft;
00495                         S32 local_y = y - mUpArrowBtn->getRect().mBottom;
00496                         handled = mUpArrowBtn->handleHover(local_x, local_y, mask);
00497                 }
00498                 else if (mDownArrowBtn->getRect().pointInRect(x, y))
00499                 {
00500                         S32 local_x = x - mDownArrowBtn->getRect().mLeft;
00501                         S32 local_y = y - mDownArrowBtn->getRect().mBottom;
00502                         handled = mDownArrowBtn->handleHover(local_x, local_y, mask);
00503                 }
00504         }
00505         if (!handled)
00506         {
00507                 handled = LLPanel::handleHover(x, y, mask);
00508         }
00509 
00510         commitHoveredButton(x, y);
00511         return handled;
00512 }
00513 
00514 BOOL LLTabContainerVertical::handleMouseUp( S32 x, S32 y, MASK mask )
00515 {
00516         BOOL handled = FALSE;
00517         BOOL has_scroll_arrows = (mMaxScrollPos > 0);
00518 
00519         if (has_scroll_arrows)
00520         {
00521                 if (mUpArrowBtn->getRect().pointInRect(x, y))
00522                 {
00523                         S32 local_x = x - mUpArrowBtn->getRect().mLeft;
00524                         S32 local_y = y - mUpArrowBtn->getRect().mBottom;
00525                         handled = mUpArrowBtn->handleMouseUp(local_x, local_y, mask);
00526                 }
00527                 else if (mDownArrowBtn->getRect().pointInRect(x, y))
00528                 {
00529                         S32 local_x = x - mDownArrowBtn->getRect().mLeft;
00530                         S32 local_y = y - mDownArrowBtn->getRect().mBottom;
00531                         handled = mDownArrowBtn->handleMouseUp(local_x, local_y, mask);
00532                 }
00533         }
00534         if (!handled)
00535         {
00536                 handled = LLPanel::handleMouseUp( x, y, mask );
00537         }
00538 
00539         commitHoveredButton(x, y);
00540         LLPanel* cur_panel = getCurrentPanel();
00541         if (hasMouseCapture())
00542         {
00543                 if (cur_panel)
00544                 {
00545                         if (!cur_panel->focusFirstItem(FALSE))
00546                         {
00547                                 mTabList[getCurrentPanelIndex()]->mButton->setFocus(TRUE);
00548                         }
00549                 }
00550                 gFocusMgr.setMouseCapture(NULL);
00551         }
00552 
00553         return handled;
00554 }
00555 
00556 BOOL LLTabContainerVertical::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
00557 {
00558         BOOL handled = FALSE;
00559         if (getEnabled())
00560         {
00561                 if (key == KEY_LEFT && mask == MASK_ALT)
00562                 {
00563                         selectPrevTab();
00564                         handled = TRUE;
00565                 }
00566                 else if (key == KEY_RIGHT && mask == MASK_ALT)
00567                 {
00568                         selectNextTab();
00569                         handled = TRUE;
00570                 }
00571 
00572                 // focus is on button
00573                 if (!handled && !gFocusMgr.childHasKeyboardFocus(getCurrentPanel()))
00574                 {
00575                         switch(key)
00576                         {
00577                         case KEY_UP:
00578                                 selectPrevTab();
00579                                 handled = TRUE;
00580                                 break;
00581                         case KEY_DOWN:
00582                                 selectNextTab();
00583                                 handled = TRUE;
00584                                 break;
00585                         case KEY_LEFT:
00586                                 handled = TRUE;
00587                                 break;
00588                         case KEY_RIGHT:
00589                                 if (getTabPosition() == LEFT && getCurrentPanel())
00590                                 {
00591                                         getCurrentPanel()->setFocus(TRUE);
00592                                 }
00593                                 handled = TRUE;
00594                                 break;
00595                         default:
00596                                 break;
00597                         }
00598                 }
00599         }
00600         return handled;
00601 }
00602 
00603 // virtual
00604 LLXMLNodePtr LLTabContainerVertical::getXML(bool save_children) const
00605 {
00606         LLXMLNodePtr node = LLTabContainerCommon::getXML();
00607 
00608         // TomY TODO Is this redundant or will it be used later?
00609         node->createChild("tab_position", TRUE)->setStringValue("left");
00610 
00611         return node;
00612 }

Generated on Thu Jul 1 06:09:15 2010 for Second Life Viewer by  doxygen 1.4.7