llcombobox.cpp

Go to the documentation of this file.
00001 
00032 // A control that displays the name of the chosen item, which when
00033 // clicked shows a scrolling box of options.
00034 
00035 #include "linden_common.h"
00036 
00037 // file includes
00038 #include "llcombobox.h"
00039 
00040 // common includes
00041 #include "llstring.h"
00042 
00043 // newview includes
00044 #include "llbutton.h"
00045 #include "llkeyboard.h"
00046 #include "llscrolllistctrl.h"
00047 #include "llwindow.h"
00048 #include "llfloater.h"
00049 #include "llscrollbar.h"
00050 #include "llcontrol.h"
00051 #include "llfocusmgr.h"
00052 #include "lllineeditor.h"
00053 #include "v2math.h"
00054 
00055 // Globals
00056 S32 LLCOMBOBOX_HEIGHT = 0;
00057 S32 LLCOMBOBOX_WIDTH = 0;
00058 S32 MAX_COMBO_WIDTH = 500;
00059 
00060 static LLRegisterWidget<LLComboBox> r1("combo_box");
00061 
00062 LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString& label,
00063         void (*commit_callback)(LLUICtrl*,void*),
00064         void *callback_userdata
00065         )
00066 :       LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata, 
00067                          FOLLOWS_LEFT | FOLLOWS_TOP),
00068         mTextEntry(NULL),
00069         mArrowImage(NULL),
00070         mAllowTextEntry(FALSE),
00071         mMaxChars(20),
00072         mTextEntryTentative(TRUE),
00073         mListPosition(BELOW),
00074         mPrearrangeCallback( NULL ),
00075         mTextEntryCallback( NULL )
00076 {
00077         // Always use text box 
00078         // Text label button
00079         mButton = new LLButton(label,
00080                                                                 LLRect(), 
00081                                                                 LLString::null,
00082                                                                 NULL, this);
00083         mButton->setImageUnselected("square_btn_32x128.tga");
00084         mButton->setImageSelected("square_btn_selected_32x128.tga");
00085         mButton->setImageDisabled("square_btn_32x128.tga");
00086         mButton->setImageDisabledSelected("square_btn_selected_32x128.tga");
00087         mButton->setScaleImage(TRUE);
00088 
00089         mButton->setMouseDownCallback(onButtonDown);
00090         mButton->setFont(LLFontGL::sSansSerifSmall);
00091         mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT);
00092         mButton->setHAlign( LLFontGL::LEFT );
00093         mButton->setRightHPad(2);
00094         addChild(mButton);
00095 
00096         // disallow multiple selection
00097         mList = new LLScrollListCtrl(
00098                 "ComboBox", LLRect(), 
00099                 &LLComboBox::onItemSelected, this, FALSE);
00100         mList->setVisible(FALSE);
00101         mList->setBgWriteableColor( LLColor4(1,1,1,1) );
00102         mList->setCommitOnKeyboardMovement(FALSE);
00103         addChild(mList);
00104 
00105         mArrowImage = LLUI::sImageProvider->getUIImage("combobox_arrow.tga");
00106         mButton->setImageOverlay("combobox_arrow.tga", LLFontGL::RIGHT);
00107 
00108         updateLayout();
00109 }
00110 
00111 
00112 LLComboBox::~LLComboBox()
00113 {
00114         // children automatically deleted, including mMenu, mButton
00115 }
00116 
00117 // virtual
00118 LLXMLNodePtr LLComboBox::getXML(bool save_children) const
00119 {
00120         LLXMLNodePtr node = LLUICtrl::getXML();
00121 
00122         // Attributes
00123 
00124         node->createChild("allow_text_entry", TRUE)->setBoolValue(mAllowTextEntry);
00125 
00126         node->createChild("max_chars", TRUE)->setIntValue(mMaxChars);
00127 
00128         // Contents
00129 
00130         std::vector<LLScrollListItem*> data_list = mList->getAllData();
00131         std::vector<LLScrollListItem*>::iterator data_itor;
00132         for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
00133         {
00134                 LLScrollListItem* item = *data_itor;
00135                 LLScrollListCell* cell = item->getColumn(0);
00136                 if (cell)
00137                 {
00138                         LLXMLNodePtr item_node = node->createChild("combo_item", FALSE);
00139                         LLSD value = item->getValue();
00140                         item_node->createChild("value", TRUE)->setStringValue(value.asString());
00141                         item_node->createChild("enabled", TRUE)->setBoolValue(item->getEnabled());
00142                         item_node->setStringValue(cell->getValue().asString());
00143                 }
00144         }
00145 
00146         return node;
00147 }
00148 
00149 // static
00150 LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
00151 {
00152         LLString name("combo_box");
00153         node->getAttributeString("name", name);
00154 
00155         LLString label("");
00156         node->getAttributeString("label", label);
00157 
00158         LLRect rect;
00159         createRect(node, rect, parent, LLRect());
00160 
00161         BOOL allow_text_entry = FALSE;
00162         node->getAttributeBOOL("allow_text_entry", allow_text_entry);
00163 
00164         S32 max_chars = 20;
00165         node->getAttributeS32("max_chars", max_chars);
00166 
00167         LLUICtrlCallback callback = NULL;
00168 
00169         LLComboBox* combo_box = new LLComboBox(name,
00170                                                         rect, 
00171                                                         label,
00172                                                         callback,
00173                                                         NULL);
00174         combo_box->setAllowTextEntry(allow_text_entry, max_chars);
00175 
00176         combo_box->initFromXML(node, parent);
00177 
00178         const LLString& contents = node->getValue();
00179 
00180         if (contents.find_first_not_of(" \n\t") != contents.npos)
00181         {
00182                 llerrs << "Legacy combo box item format used! Please convert to <combo_item> tags!" << llendl;
00183         }
00184         else
00185         {
00186                 LLXMLNodePtr child;
00187                 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
00188                 {
00189                         if (child->hasName("combo_item"))
00190                         {
00191                                 LLString label = child->getTextContents();
00192 
00193                                 LLString value = label;
00194                                 child->getAttributeString("value", value);
00195 
00196                                 combo_box->add(label, LLSD(value) );
00197                         }
00198                 }
00199         }
00200 
00201         combo_box->selectFirstItem();
00202 
00203         return combo_box;
00204 }
00205 
00206 void LLComboBox::setEnabled(BOOL enabled)
00207 {
00208         LLView::setEnabled(enabled);
00209         mButton->setEnabled(enabled);
00210 }
00211 
00212 void LLComboBox::clear()
00213 { 
00214         if (mTextEntry)
00215         {
00216                 mTextEntry->setText(LLString::null);
00217         }
00218         mButton->setLabelSelected(LLString::null);
00219         mButton->setLabelUnselected(LLString::null);
00220         mButton->setDisabledLabel(LLString::null);
00221         mButton->setDisabledSelectedLabel(LLString::null);
00222         mList->deselectAllItems();
00223 }
00224 
00225 void LLComboBox::onCommit()
00226 {
00227         if (mAllowTextEntry && getCurrentIndex() != -1)
00228         {
00229                 // we have selected an existing item, blitz the manual text entry with
00230                 // the properly capitalized item
00231                 mTextEntry->setValue(getSimple());
00232                 mTextEntry->setTentative(FALSE);
00233         }
00234         LLUICtrl::onCommit();
00235 }
00236 
00237 // virtual
00238 BOOL LLComboBox::isDirty() const
00239 {
00240         BOOL grubby = FALSE;
00241         if ( mList )
00242         {
00243                 grubby = mList->isDirty();
00244         }
00245         return grubby;
00246 }
00247 
00248 // virtual   Clear dirty state
00249 void    LLComboBox::resetDirty()
00250 {
00251         if ( mList )
00252         {
00253                 mList->resetDirty();
00254         }
00255 }
00256 
00257 
00258 // add item "name" to menu
00259 LLScrollListItem* LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled)
00260 {
00261         LLScrollListItem* item = mList->addSimpleElement(name, pos);
00262         item->setEnabled(enabled);
00263         mList->selectFirstItem();
00264         return item;
00265 }
00266 
00267 // add item "name" with a unique id to menu
00268 LLScrollListItem* LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled )
00269 {
00270         LLScrollListItem* item = mList->addSimpleElement(name, pos, id);
00271         item->setEnabled(enabled);
00272         mList->selectFirstItem();
00273         return item;
00274 }
00275 
00276 // add item "name" with attached userdata
00277 LLScrollListItem* LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled )
00278 {
00279         LLScrollListItem* item = mList->addSimpleElement(name, pos);
00280         item->setEnabled(enabled);
00281         item->setUserdata( userdata );
00282         mList->selectFirstItem();
00283         return item;
00284 }
00285 
00286 // add item "name" with attached generic data
00287 LLScrollListItem* LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled )
00288 {
00289         LLScrollListItem* item = mList->addSimpleElement(name, pos, value);
00290         item->setEnabled(enabled);
00291         mList->selectFirstItem();
00292         return item;
00293 }
00294 
00295 LLScrollListItem* LLComboBox::addSeparator(EAddPosition pos)
00296 {
00297         return mList->addSeparator(pos);
00298 }
00299 
00300 void LLComboBox::sortByName()
00301 {
00302         mList->sortByColumn(0, TRUE);
00303 }
00304 
00305 
00306 // Choose an item with a given name in the menu.
00307 // Returns TRUE if the item was found.
00308 BOOL LLComboBox::setSimple(const LLStringExplicit& name)
00309 {
00310         BOOL found = mList->selectItemByLabel(name, FALSE);
00311 
00312         if (found)
00313         {
00314                 setLabel(name);
00315         }
00316 
00317         return found;
00318 }
00319 
00320 // virtual
00321 void LLComboBox::setValue(const LLSD& value)
00322 {
00323         BOOL found = mList->selectByValue(value);
00324         if (found)
00325         {
00326                 LLScrollListItem* item = mList->getFirstSelected();
00327                 if (item)
00328                 {
00329                         setLabel( mList->getSelectedItemLabel() );
00330                 }
00331         }
00332 }
00333 
00334 const LLString LLComboBox::getSimple() const
00335 {
00336         const LLString res = mList->getSelectedItemLabel();
00337         if (res.empty() && mAllowTextEntry)
00338         {
00339                 return mTextEntry->getText();
00340         }
00341         else
00342         {
00343                 return res;
00344         }
00345 }
00346 
00347 const LLString LLComboBox::getSelectedItemLabel(S32 column) const
00348 {
00349         return mList->getSelectedItemLabel(column);
00350 }
00351 
00352 // virtual
00353 LLSD LLComboBox::getValue() const
00354 {
00355         LLScrollListItem* item = mList->getFirstSelected();
00356         if( item )
00357         {
00358                 return item->getValue();
00359         }
00360         else if (mAllowTextEntry)
00361         {
00362                 return mTextEntry->getValue();
00363         }
00364         else
00365         {
00366                 return LLSD();
00367         }
00368 }
00369 
00370 void LLComboBox::setLabel(const LLStringExplicit& name)
00371 {
00372         if ( mTextEntry )
00373         {
00374                 mTextEntry->setText(name);
00375                 if (mList->selectItemByLabel(name, FALSE))
00376                 {
00377                         mTextEntry->setTentative(FALSE);
00378                 }
00379                 else
00380                 {
00381                         mTextEntry->setTentative(mTextEntryTentative);
00382                 }
00383         }
00384         
00385         if (!mAllowTextEntry)
00386         {
00387                 mButton->setLabelUnselected(name);
00388                 mButton->setLabelSelected(name);
00389                 mButton->setDisabledLabel(name);
00390                 mButton->setDisabledSelectedLabel(name);
00391         }
00392 }
00393 
00394 
00395 BOOL LLComboBox::remove(const LLString& name)
00396 {
00397         BOOL found = mList->selectItemByLabel(name);
00398 
00399         if (found)
00400         {
00401                 LLScrollListItem* item = mList->getFirstSelected();
00402                 if (item)
00403                 {
00404                         mList->deleteSingleItem(mList->getItemIndex(item));
00405                 }
00406         }
00407 
00408         return found;
00409 }
00410 
00411 BOOL LLComboBox::remove(S32 index)
00412 {
00413         if (index < mList->getItemCount())
00414         {
00415                 mList->deleteSingleItem(index);
00416                 return TRUE;
00417         }
00418         return FALSE;
00419 }
00420 
00421 // Keyboard focus lost.
00422 void LLComboBox::onFocusLost()
00423 {
00424         hideList();
00425         // if valid selection
00426         if (mAllowTextEntry && getCurrentIndex() != -1)
00427         {
00428                 mTextEntry->selectAll();
00429         }
00430         LLUICtrl::onFocusLost();
00431 }
00432 
00433 void LLComboBox::onLostTop()
00434 {
00435         hideList();
00436 }
00437 
00438 
00439 void LLComboBox::setButtonVisible(BOOL visible)
00440 {
00441         mButton->setVisible(visible);
00442         if (mTextEntry)
00443         {
00444                 LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
00445                 if (visible)
00446                 {
00447                         text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
00448                 }
00449                 //mTextEntry->setRect(text_entry_rect);
00450                 mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
00451         }
00452 }
00453 
00454 void LLComboBox::draw()
00455 {
00456         mButton->setEnabled(getEnabled() /*&& !mList->isEmpty()*/);
00457 
00458         // Draw children normally
00459         LLUICtrl::draw();
00460 }
00461 
00462 BOOL LLComboBox::setCurrentByIndex( S32 index )
00463 {
00464         BOOL found = mList->selectNthItem( index );
00465         if (found)
00466         {
00467                 setLabel(mList->getSelectedItemLabel());
00468         }
00469         return found;
00470 }
00471 
00472 S32 LLComboBox::getCurrentIndex() const
00473 {
00474         LLScrollListItem* item = mList->getFirstSelected();
00475         if( item )
00476         {
00477                 return mList->getItemIndex( item );
00478         }
00479         return -1;
00480 }
00481 
00482 
00483 void LLComboBox::updateLayout()
00484 {
00485         LLRect rect = getLocalRect();
00486         if (mAllowTextEntry)
00487         {
00488                 S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton");
00489                 mButton->setRect(LLRect( getRect().getWidth() - llmax(8,mArrowImage->getWidth()) - 2 * shadow_size,
00490                                                                 rect.mTop, rect.mRight, rect.mBottom));
00491                 mButton->setTabStop(FALSE);
00492 
00493                 if (!mTextEntry)
00494                 {
00495                         LLRect text_entry_rect(0, getRect().getHeight(), getRect().getWidth(), 0);
00496                         text_entry_rect.mRight -= llmax(8,mArrowImage->getWidth()) + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
00497                         // clear label on button
00498                         LLString cur_label = mButton->getLabelSelected();
00499                         mTextEntry = new LLLineEditor("combo_text_entry",
00500                                                                                 text_entry_rect,
00501                                                                                 "",
00502                                                                                 LLFontGL::sSansSerifSmall,
00503                                                                                 mMaxChars,
00504                                                                                 onTextCommit,
00505                                                                                 onTextEntry,
00506                                                                                 NULL,
00507                                                                                 this);
00508                         mTextEntry->setSelectAllonFocusReceived(TRUE);
00509                         mTextEntry->setHandleEditKeysDirectly(TRUE);
00510                         mTextEntry->setCommitOnFocusLost(FALSE);
00511                         mTextEntry->setText(cur_label);
00512                         mTextEntry->setIgnoreTab(TRUE);
00513                         mTextEntry->setFollowsAll();
00514                         addChild(mTextEntry);
00515                 }
00516                 else
00517                 {
00518                         mTextEntry->setVisible(TRUE);
00519                         mTextEntry->setMaxTextLength(mMaxChars);
00520                 }
00521 
00522                 // clear label on button
00523                 setLabel(LLString::null);
00524 
00525                 mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT);
00526         }
00527         else if (!mAllowTextEntry)
00528         {
00529                 mButton->setRect(rect);
00530                 mButton->setTabStop(TRUE);
00531 
00532                 if (mTextEntry)
00533                 {
00534                         mTextEntry->setVisible(FALSE);
00535                 }
00536                 mButton->setFollowsAll();
00537         }
00538 }
00539 
00540 void* LLComboBox::getCurrentUserdata()
00541 {
00542         LLScrollListItem* item = mList->getFirstSelected();
00543         if( item )
00544         {
00545                 return item->getUserdata();
00546         }
00547         return NULL;
00548 }
00549 
00550 
00551 void LLComboBox::showList()
00552 {
00553         // Make sure we don't go off top of screen.
00554         LLCoordWindow window_size;
00555         getWindow()->getSize(&window_size);
00556         //HACK: shouldn't have to know about scale here
00557         mList->fitContents( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 );
00558 
00559         // Make sure that we can see the whole list
00560         LLRect root_view_local;
00561         LLView* root_view = getRootView();
00562         root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this);
00563         
00564         LLRect rect = mList->getRect();
00565 
00566         S32 min_width = getRect().getWidth();
00567         S32 max_width = llmax(min_width, MAX_COMBO_WIDTH);
00568         // make sure we have up to date content width metrics
00569         mList->calcColumnWidths();
00570         S32 list_width = llclamp(mList->getMaxContentWidth(), min_width, max_width);
00571 
00572         if (mListPosition == BELOW)
00573         {
00574                 if (rect.getHeight() <= -root_view_local.mBottom)
00575                 {
00576                         // Move rect so it hangs off the bottom of this view
00577                         rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() );
00578                 }
00579                 else
00580                 {       
00581                         // stack on top or bottom, depending on which has more room
00582                         if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
00583                         {
00584                                 // Move rect so it hangs off the bottom of this view
00585                                 rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
00586                         }
00587                         else
00588                         {
00589                                 // move rect so it stacks on top of this view (clipped to size of screen)
00590                                 rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
00591                         }
00592                 }
00593         }
00594         else // ABOVE
00595         {
00596                 if (rect.getHeight() <= root_view_local.mTop - getRect().getHeight())
00597                 {
00598                         // move rect so it stacks on top of this view (clipped to size of screen)
00599                         rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
00600                 }
00601                 else
00602                 {
00603                         // stack on top or bottom, depending on which has more room
00604                         if (-root_view_local.mBottom > root_view_local.mTop - getRect().getHeight())
00605                         {
00606                                 // Move rect so it hangs off the bottom of this view
00607                                 rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
00608                         }
00609                         else
00610                         {
00611                                 // move rect so it stacks on top of this view (clipped to size of screen)
00612                                 rect.setOriginAndSize(0, getRect().getHeight(), list_width, llmin(root_view_local.mTop - getRect().getHeight(), rect.getHeight()));
00613                         }
00614                 }
00615 
00616         }
00617         mList->setOrigin(rect.mLeft, rect.mBottom);
00618         mList->reshape(rect.getWidth(), rect.getHeight());
00619         mList->translateIntoRect(root_view_local, FALSE);
00620 
00621         // Make sure we didn't go off bottom of screen
00622         S32 x, y;
00623         mList->localPointToScreen(0, 0, &x, &y);
00624 
00625         if (y < 0)
00626         {
00627                 mList->translate(0, -y);
00628         }
00629 
00630         // NB: this call will trigger the focuslost callback which will hide the list, so do it first
00631         // before finally showing the list
00632 
00633         mList->setFocus(TRUE);
00634 
00635         // register ourselves as a "top" control
00636         // effectively putting us into a special draw layer
00637         // and not affecting the bounding rectangle calculation
00638         gFocusMgr.setTopCtrl(this);
00639 
00640         // Show the list and push the button down
00641         mButton->setToggleState(TRUE);
00642         mList->setVisible(TRUE);
00643         
00644         setUseBoundingRect(TRUE);
00645 }
00646 
00647 void LLComboBox::hideList()
00648 {
00649         //*HACK: store the original value explicitly somewhere, not just in label
00650         LLString orig_selection = mAllowTextEntry ? mTextEntry->getText() : mButton->getLabelSelected();
00651 
00652         // assert selection in list
00653         mList->selectItemByLabel(orig_selection, FALSE);
00654 
00655         mButton->setToggleState(FALSE);
00656         mList->setVisible(FALSE);
00657         mList->highlightNthItem(-1);
00658 
00659         setUseBoundingRect(FALSE);
00660         if( gFocusMgr.getTopCtrl() == this )
00661         {
00662                 gFocusMgr.setTopCtrl(NULL);
00663         }
00664 }
00665 
00666 //------------------------------------------------------------------
00667 // static functions
00668 //------------------------------------------------------------------
00669 
00670 // static
00671 void LLComboBox::onButtonDown(void *userdata)
00672 {
00673         LLComboBox *self = (LLComboBox *)userdata;
00674 
00675         if (!self->mList->getVisible())
00676         {
00677                 LLScrollListItem* last_selected_item = self->mList->getLastSelectedItem();
00678                 if (last_selected_item)
00679                 {
00680                         // highlight the original selection before potentially selecting a new item
00681                         self->mList->highlightNthItem(self->mList->getItemIndex(last_selected_item));
00682                 }
00683 
00684                 if( self->mPrearrangeCallback )
00685                 {
00686                         self->mPrearrangeCallback( self, self->mCallbackUserData );
00687                 }
00688 
00689                 if (self->mList->getItemCount() != 0)
00690                 {
00691                         self->showList();
00692                 }
00693 
00694                 self->setFocus( TRUE );
00695 
00696                 // pass mouse capture on to list if button is depressed
00697                 if (self->mButton->hasMouseCapture())
00698                 {
00699                         gFocusMgr.setMouseCapture(self->mList);
00700                 }
00701         }
00702         else
00703         {
00704                 self->hideList();
00705         } 
00706 
00707 }
00708 
00709 // static
00710 void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata)
00711 {
00712         // Note: item is the LLScrollListCtrl
00713         LLComboBox *self = (LLComboBox *) userdata;
00714 
00715         const LLString name = self->mList->getSelectedItemLabel();
00716 
00717         S32 cur_id = self->getCurrentIndex();
00718         if (cur_id != -1)
00719         {
00720                 self->setLabel(name);
00721 
00722                 if (self->mAllowTextEntry)
00723                 {
00724                         gFocusMgr.setKeyboardFocus(self->mTextEntry);
00725                         self->mTextEntry->selectAll();
00726                 }
00727         }
00728 
00729         // hiding the list reasserts the old value stored in the text editor/dropdown button
00730         self->hideList();
00731 
00732         // commit does the reverse, asserting the value in the list
00733         self->onCommit();
00734 }
00735 
00736 BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
00737 {
00738     LLString tool_tip;
00739 
00740         if(LLUICtrl::handleToolTip(x, y, msg, sticky_rect_screen))
00741         {
00742                 return TRUE;
00743         }
00744         
00745         if (LLUI::sShowXUINames)
00746         {
00747                 tool_tip = getShowNamesToolTip();
00748         }
00749         else
00750         {
00751                 tool_tip = getToolTip();
00752                 if (tool_tip.empty())
00753                 {
00754                         tool_tip = getSelectedItemLabel();
00755                 }
00756         }
00757         
00758         if( !tool_tip.empty() )
00759         {
00760                 msg = tool_tip;
00761 
00762                 // Convert rect local to screen coordinates
00763                 localPointToScreen( 
00764                         0, 0, 
00765                         &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
00766                 localPointToScreen(
00767                         getRect().getWidth(), getRect().getHeight(),
00768                         &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
00769         }
00770         return TRUE;
00771 }
00772 
00773 BOOL LLComboBox::handleKeyHere(KEY key, MASK mask)
00774 {
00775         BOOL result = FALSE;
00776         if (hasFocus())
00777         {
00778                 //give list a chance to pop up and handle key
00779                 LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
00780                 if (last_selected_item)
00781                 {
00782                         // highlight the original selection before potentially selecting a new item
00783                         mList->highlightNthItem(mList->getItemIndex(last_selected_item));
00784                 }
00785                 result = mList->handleKeyHere(key, mask);
00786                 // if selection has changed, pop open list
00787                 if (mList->getLastSelectedItem() != last_selected_item)
00788                 {
00789                         showList();
00790                 }
00791         }
00792         return result;
00793 }
00794 
00795 BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char)
00796 {
00797         BOOL result = FALSE;
00798         if (gFocusMgr.childHasKeyboardFocus(this))
00799         {
00800                 // space bar just shows the list
00801                 if (' ' != uni_char )
00802                 {
00803                         LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
00804                         if (last_selected_item)
00805                         {
00806                                 // highlight the original selection before potentially selecting a new item
00807                                 mList->highlightNthItem(mList->getItemIndex(last_selected_item));
00808                         }
00809                         result = mList->handleUnicodeCharHere(uni_char);
00810                         if (mList->getLastSelectedItem() != last_selected_item)
00811                         {
00812                                 showList();
00813                         }
00814                 }
00815         }
00816         return result;
00817 }
00818 
00819 void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative)
00820 {
00821         mAllowTextEntry = allow;
00822         mTextEntryTentative = set_tentative;
00823         mMaxChars = max_chars;
00824 
00825         updateLayout();
00826 }
00827 
00828 void LLComboBox::setTextEntry(const LLStringExplicit& text)
00829 {
00830         if (mTextEntry)
00831         {
00832                 mTextEntry->setText(text);
00833                 updateSelection();
00834         }
00835 }
00836 
00837 //static 
00838 void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data)
00839 {
00840         LLComboBox* self = (LLComboBox*)user_data;
00841 
00842         if (self->mTextEntryCallback)
00843         {
00844                 (*self->mTextEntryCallback)(line_editor, self->mCallbackUserData);
00845         }
00846 
00847         KEY key = gKeyboard->currentKey();
00848         if (key == KEY_BACKSPACE || 
00849                 key == KEY_DELETE)
00850         {
00851                 if (self->mList->selectItemByLabel(line_editor->getText(), FALSE))
00852                 {
00853                         line_editor->setTentative(FALSE);
00854                 }
00855                 else
00856                 {
00857                         line_editor->setTentative(self->mTextEntryTentative);
00858                         self->mList->deselectAllItems();
00859                 }
00860                 return;
00861         }
00862 
00863         if (key == KEY_LEFT || 
00864                 key == KEY_RIGHT)
00865         {
00866                 return;
00867         }
00868 
00869         if (key == KEY_DOWN)
00870         {
00871                 self->setCurrentByIndex(llmin(self->getItemCount() - 1, self->getCurrentIndex() + 1));
00872                 if (!self->mList->getVisible())
00873                 {
00874                         if( self->mPrearrangeCallback )
00875                         {
00876                                 self->mPrearrangeCallback( self, self->mCallbackUserData );
00877                         }
00878 
00879                         if (self->mList->getItemCount() != 0)
00880                         {
00881                                 self->showList();
00882                         }
00883                 }
00884                 line_editor->selectAll();
00885                 line_editor->setTentative(FALSE);
00886         }
00887         else if (key == KEY_UP)
00888         {
00889                 self->setCurrentByIndex(llmax(0, self->getCurrentIndex() - 1));
00890                 if (!self->mList->getVisible())
00891                 {
00892                         if( self->mPrearrangeCallback )
00893                         {
00894                                 self->mPrearrangeCallback( self, self->mCallbackUserData );
00895                         }
00896 
00897                         if (self->mList->getItemCount() != 0)
00898                         {
00899                                 self->showList();
00900                         }
00901                 }
00902                 line_editor->selectAll();
00903                 line_editor->setTentative(FALSE);
00904         }
00905         else
00906         {
00907                 // RN: presumably text entry
00908                 self->updateSelection();
00909         }
00910 }
00911 
00912 void LLComboBox::updateSelection()
00913 {
00914         LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor());
00915         // user-entered portion of string, based on assumption that any selected
00916     // text was a result of auto-completion
00917         LLWString user_wstring = mTextEntry->hasSelection() ? left_wstring : mTextEntry->getWText();
00918         LLString full_string = mTextEntry->getText();
00919 
00920         // go ahead and arrange drop down list on first typed character, even
00921         // though we aren't showing it... some code relies on prearrange
00922         // callback to populate content
00923         if( mTextEntry->getWText().size() == 1 )
00924         {
00925                 if (mPrearrangeCallback)
00926                 {
00927                         mPrearrangeCallback( this, mCallbackUserData );
00928                 }
00929         }
00930 
00931         if (mList->selectItemByLabel(full_string, FALSE))
00932         {
00933                 mTextEntry->setTentative(FALSE);
00934         }
00935         else if (!mList->selectItemByPrefix(left_wstring, FALSE))
00936         {
00937                 mList->deselectAllItems();
00938                 mTextEntry->setText(wstring_to_utf8str(user_wstring));
00939                 mTextEntry->setTentative(mTextEntryTentative);
00940         }
00941         else
00942         {
00943                 LLWString selected_item = utf8str_to_wstring(mList->getSelectedItemLabel());
00944                 LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size());
00945                 mTextEntry->setText(wstring_to_utf8str(wtext));
00946                 mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size());
00947                 mTextEntry->endSelection();
00948                 mTextEntry->setTentative(FALSE);
00949         }
00950 }
00951 
00952 //static 
00953 void LLComboBox::onTextCommit(LLUICtrl* caller, void* user_data)
00954 {
00955         LLComboBox* self = (LLComboBox*)user_data;
00956         LLString text = self->mTextEntry->getText();
00957         self->setSimple(text);
00958         self->onCommit();
00959         self->mTextEntry->selectAll();
00960 }
00961 
00962 void LLComboBox::setFocus(BOOL b)
00963 {
00964         LLUICtrl::setFocus(b);
00965 
00966         if (b)
00967         {
00968                 mList->clearSearchString();
00969                 if (mList->getVisible())
00970                 {
00971                         mList->setFocus(TRUE);
00972                 }
00973         }
00974 }
00975 
00976 //============================================================================
00977 // LLCtrlListInterface functions
00978 
00979 S32 LLComboBox::getItemCount() const
00980 {
00981         return mList->getItemCount();
00982 }
00983 
00984 void LLComboBox::addColumn(const LLSD& column, EAddPosition pos)
00985 {
00986         mList->clearColumns();
00987         mList->addColumn(column, pos);
00988 }
00989 
00990 void LLComboBox::clearColumns()
00991 {
00992         mList->clearColumns();
00993 }
00994 
00995 void LLComboBox::setColumnLabel(const LLString& column, const LLString& label)
00996 {
00997         mList->setColumnLabel(column, label);
00998 }
00999 
01000 LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata)
01001 {
01002         return mList->addElement(value, pos, userdata);
01003 }
01004 
01005 LLScrollListItem* LLComboBox::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id)
01006 {
01007         return mList->addSimpleElement(value, pos, id);
01008 }
01009 
01010 void LLComboBox::clearRows()
01011 {
01012         mList->clearRows();
01013 }
01014 
01015 void LLComboBox::sortByColumn(LLString name, BOOL ascending)
01016 {
01017 }
01018 
01019 //============================================================================
01020 //LLCtrlSelectionInterface functions
01021 
01022 BOOL LLComboBox::setCurrentByID(const LLUUID& id)
01023 {
01024         BOOL found = mList->selectByID( id );
01025 
01026         if (found)
01027         {
01028                 setLabel(mList->getSelectedItemLabel());
01029         }
01030 
01031         return found;
01032 }
01033 
01034 LLUUID LLComboBox::getCurrentID() const
01035 {
01036         return mList->getStringUUIDSelectedItem();
01037 }
01038 BOOL LLComboBox::setSelectedByValue(const LLSD& value, BOOL selected)
01039 {
01040         BOOL found = mList->setSelectedByValue(value, selected);
01041         if (found)
01042         {
01043                 setLabel(mList->getSelectedItemLabel());
01044         }
01045         return found;
01046 }
01047 
01048 LLSD LLComboBox::getSelectedValue()
01049 {
01050         return mList->getSelectedValue();
01051 }
01052 
01053 BOOL LLComboBox::isSelected(const LLSD& value) const
01054 {
01055         return mList->isSelected(value);
01056 }
01057 
01058 BOOL LLComboBox::operateOnSelection(EOperation op)
01059 {
01060         if (op == OP_DELETE)
01061         {
01062                 mList->deleteSelectedItems();
01063                 return TRUE;
01064         }
01065         return FALSE;
01066 }
01067 
01068 BOOL LLComboBox::operateOnAll(EOperation op)
01069 {
01070         if (op == OP_DELETE)
01071         {
01072                 clearRows();
01073                 return TRUE;
01074         }
01075         return FALSE;
01076 }
01077 
01078 BOOL LLComboBox::selectItemRange( S32 first, S32 last )
01079 {
01080         return mList->selectItemRange(first, last);
01081 }
01082 
01083 
01084 //
01085 // LLFlyoutButton
01086 //
01087 
01088 static LLRegisterWidget<LLFlyoutButton> r2("flyout_button");
01089 
01090 const S32 FLYOUT_BUTTON_ARROW_WIDTH = 24;
01091 
01092 LLFlyoutButton::LLFlyoutButton(
01093                 const LLString& name, 
01094                 const LLRect &rect,
01095                 const LLString& label,
01096                 void (*commit_callback)(LLUICtrl*, void*) ,
01097                 void *callback_userdata)
01098 :               LLComboBox(name, rect, LLString::null, commit_callback, callback_userdata),
01099                 mToggleState(FALSE),
01100                 mActionButton(NULL)
01101 {
01102         // Always use text box 
01103         // Text label button
01104         mActionButton = new LLButton(label,
01105                                         LLRect(), LLString::null, NULL, this);
01106         mActionButton->setScaleImage(TRUE);
01107 
01108         mActionButton->setClickedCallback(onActionButtonClick);
01109         mActionButton->setFollowsAll();
01110         mActionButton->setHAlign( LLFontGL::HCENTER );
01111         mActionButton->setLabel(label);
01112         addChild(mActionButton);
01113 
01114         mActionButtonImage = LLUI::getUIImage("flyout_btn_left.tga");
01115         mExpanderButtonImage = LLUI::getUIImage("flyout_btn_right.tga");
01116         mActionButtonImageSelected = LLUI::getUIImage("flyout_btn_left_selected.tga");
01117         mExpanderButtonImageSelected = LLUI::getUIImage("flyout_btn_right_selected.tga");
01118         mActionButtonImageDisabled = LLUI::getUIImage("flyout_btn_left_disabled.tga");
01119         mExpanderButtonImageDisabled = LLUI::getUIImage("flyout_btn_right_disabled.tga");
01120 
01121         mActionButton->setImageSelected(mActionButtonImageSelected);
01122         mActionButton->setImageUnselected(mActionButtonImage);
01123         mActionButton->setImageDisabled(mActionButtonImageDisabled);
01124         mActionButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL));
01125 
01126         mButton->setImageSelected(mExpanderButtonImageSelected);
01127         mButton->setImageUnselected(mExpanderButtonImage);
01128         mButton->setImageDisabled(mExpanderButtonImageDisabled);
01129         mButton->setImageDisabledSelected(LLPointer<LLUIImage>(NULL));
01130         mButton->setRightHPad(6);
01131 
01132         updateLayout();
01133 }
01134 
01135 //static 
01136 LLView* LLFlyoutButton::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
01137 {
01138         LLString name = "flyout_button";
01139         node->getAttributeString("name", name);
01140 
01141         LLString label("");
01142         node->getAttributeString("label", label);
01143 
01144         LLRect rect;
01145         createRect(node, rect, parent, LLRect());
01146 
01147         LLUICtrlCallback callback = NULL;
01148 
01149         LLFlyoutButton* flyout_button = new LLFlyoutButton(name,
01150                                                         rect, 
01151                                                         label,
01152                                                         callback,
01153                                                         NULL);
01154 
01155         LLString list_position;
01156         node->getAttributeString("list_position", list_position);
01157         if (list_position == "below")
01158         {
01159                 flyout_button->mListPosition = BELOW;
01160         }
01161         else if (list_position == "above")
01162         {
01163                 flyout_button->mListPosition = ABOVE;
01164         }
01165         
01166 
01167         flyout_button->initFromXML(node, parent);
01168 
01169         LLXMLNodePtr child;
01170         for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
01171         {
01172                 if (child->hasName("flyout_button_item"))
01173                 {
01174                         LLString label = child->getTextContents();
01175 
01176                         LLString value = label;
01177                         child->getAttributeString("value", value);
01178 
01179                         flyout_button->add(label, LLSD(value) );
01180                 }
01181         }
01182 
01183         flyout_button->updateLayout();
01184 
01185         return flyout_button;
01186 }
01187 
01188 void LLFlyoutButton::updateLayout()
01189 {
01190         LLComboBox::updateLayout();
01191 
01192         mButton->setOrigin(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, 0);
01193         mButton->reshape(FLYOUT_BUTTON_ARROW_WIDTH, getRect().getHeight());
01194         mButton->setFollows(FOLLOWS_RIGHT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
01195         mButton->setTabStop(FALSE);
01196         mButton->setImageOverlay(mListPosition == BELOW ? "down_arrow.tga" : "up_arrow.tga", LLFontGL::RIGHT);
01197 
01198         mActionButton->setOrigin(0, 0);
01199         mActionButton->reshape(getRect().getWidth() - FLYOUT_BUTTON_ARROW_WIDTH, getRect().getHeight());
01200 }
01201 
01202 //static 
01203 void LLFlyoutButton::onActionButtonClick(void *user_data)
01204 {
01205         LLFlyoutButton* buttonp = (LLFlyoutButton*)user_data;
01206         // remember last list selection?
01207         buttonp->mList->deselect();
01208         buttonp->onCommit();
01209 }
01210 
01211 void LLFlyoutButton::draw()
01212 {
01213         mActionButton->setToggleState(mToggleState);
01214         mButton->setToggleState(mToggleState);
01215 
01216         //FIXME: this should be an attribute of comboboxes, whether they have a distinct label or
01217         // the label reflects the last selected item, for now we have to manually remove the label
01218         mButton->setLabel(LLString::null);
01219         LLComboBox::draw();     
01220 }
01221 
01222 void LLFlyoutButton::setEnabled(BOOL enabled)
01223 {
01224         mActionButton->setEnabled(enabled);
01225         LLComboBox::setEnabled(enabled);
01226 }
01227 
01228 
01229 void LLFlyoutButton::setToggleState(BOOL state)
01230 {
01231         mToggleState = state;
01232 }
01233 

Generated on Fri May 16 08:32:51 2008 for SecondLife by  doxygen 1.5.5