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  
00059 LLComboBox::LLComboBox( const LLString& name, const LLRect &rect, const LLString& label,
00060         void (*commit_callback)(LLUICtrl*,void*),
00061         void *callback_userdata
00062         )
00063 :       LLUICtrl(name, rect, TRUE, commit_callback, callback_userdata, 
00064                          FOLLOWS_LEFT | FOLLOWS_TOP),
00065         mDrawArrow(TRUE),
00066         mTextEntry(NULL),
00067         mArrowImage(NULL),
00068         mArrowImageWidth(8),
00069         mAllowTextEntry(FALSE),
00070         mMaxChars(20),
00071         mTextEntryTentative(TRUE),
00072         mListPosition(BELOW),
00073         mPrearrangeCallback( NULL ),
00074         mTextEntryCallback( NULL )
00075 {
00076         // For now, all comboboxes don't take keyboard focus when clicked.
00077         // This might change if it is part of a modal dialog.
00078         // mKeyboardFocusOnClick = FALSE;
00079 
00080         // Revert to standard behavior.  When this control's parent is hidden, it needs to
00081         // hide this ctrl--which won't just happen automatically since when LLComboBox is 
00082         // showing its list, it's also set to TopCtrl.  When keyboard focus is cleared all
00083         // controls (including this one) know that they are no longer editing.
00084         mKeyboardFocusOnClick = TRUE;
00085 
00086         LLRect r;
00087         r.setOriginAndSize(0, 0, rect.getWidth(), rect.getHeight());
00088 
00089         // Always use text box 
00090         // Text label button
00091         mButton = new LLSquareButton("comboxbox button",
00092                                                                  r, label, NULL, LLString::null,
00093                                                                  NULL, this);
00094         mButton->setMouseDownCallback(onButtonDown);
00095         mButton->setFont(LLFontGL::sSansSerifSmall);
00096         mButton->setFollows(FOLLOWS_LEFT | FOLLOWS_BOTTOM | FOLLOWS_RIGHT);
00097         mButton->setHAlign( LLFontGL::LEFT );
00098 
00099         const S32 ARROW_WIDTH = 16;
00100         mButton->setRightHPad( ARROW_WIDTH );
00101         addChild(mButton);
00102 
00103         // Default size, will be set by arrange() call in button callback. 
00104         S32 list_width = mRect.getWidth() + SCROLLBAR_SIZE;
00105         r.setOriginAndSize(0, 16, list_width, 220);
00106 
00107         // disallow multiple selection
00108         mList = new LLScrollListCtrl(
00109                 "ComboBox", r, 
00110                 &LLComboBox::onItemSelected, this, FALSE);
00111         mList->setVisible(FALSE);
00112         mList->setBgWriteableColor( LLColor4(1,1,1,1) );
00113         mList->setCommitOnKeyboardMovement(FALSE);
00114         mList->setFocusChangedCallback(onListFocusChanged);
00115         addChild(mList);
00116 
00117         LLRect border_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
00118         mBorder = new LLViewBorder( "combo border", border_rect );
00119         addChild( mBorder );
00120         mBorder->setFollows(FOLLOWS_LEFT|FOLLOWS_RIGHT|FOLLOWS_TOP|FOLLOWS_BOTTOM);
00121 
00122         LLUUID arrow_image_id( LLUI::sAssetsGroup->getString("combobox_arrow.tga") );
00123         mArrowImage = LLUI::sImageProvider->getUIImageByID(arrow_image_id);
00124         mArrowImageWidth = llmax(8,mArrowImage->getWidth(0)); // In case image hasn't loaded yet
00125 }
00126 
00127 
00128 LLComboBox::~LLComboBox()
00129 {
00130         // children automatically deleted, including mMenu, mButton
00131 }
00132 
00133 // virtual
00134 LLXMLNodePtr LLComboBox::getXML(bool save_children) const
00135 {
00136         LLXMLNodePtr node = LLUICtrl::getXML();
00137 
00138         // Attributes
00139 
00140         node->createChild("allow_text_entry", TRUE)->setBoolValue(mAllowTextEntry);
00141 
00142         node->createChild("max_chars", TRUE)->setIntValue(mMaxChars);
00143 
00144         // Contents
00145 
00146         std::vector<LLScrollListItem*> data_list = mList->getAllData();
00147         std::vector<LLScrollListItem*>::iterator data_itor;
00148         for (data_itor = data_list.begin(); data_itor != data_list.end(); ++data_itor)
00149         {
00150                 LLScrollListItem* item = *data_itor;
00151                 LLScrollListCell* cell = item->getColumn(0);
00152                 if (cell)
00153                 {
00154                         LLXMLNodePtr item_node = node->createChild("combo_item", FALSE);
00155                         LLSD value = item->getValue();
00156                         item_node->createChild("value", TRUE)->setStringValue(value.asString());
00157                         item_node->createChild("enabled", TRUE)->setBoolValue(item->getEnabled());
00158                         item_node->setStringValue(cell->getText());
00159                 }
00160         }
00161 
00162         return node;
00163 }
00164 
00165 // static
00166 LLView* LLComboBox::fromXML(LLXMLNodePtr node, LLView *parent, LLUICtrlFactory *factory)
00167 {
00168         LLString name("combo_box");
00169         node->getAttributeString("name", name);
00170 
00171         LLString label("");
00172         node->getAttributeString("label", label);
00173 
00174         LLRect rect;
00175         createRect(node, rect, parent, LLRect());
00176 
00177         BOOL allow_text_entry = FALSE;
00178         node->getAttributeBOOL("allow_text_entry", allow_text_entry);
00179 
00180         S32 max_chars = 20;
00181         node->getAttributeS32("max_chars", max_chars);
00182 
00183         LLUICtrlCallback callback = NULL;
00184 
00185         LLComboBox* combo_box = new LLComboBox(name,
00186                                                         rect, 
00187                                                         label,
00188                                                         callback,
00189                                                         NULL);
00190         combo_box->setAllowTextEntry(allow_text_entry, max_chars);
00191 
00192         combo_box->initFromXML(node, parent);
00193 
00194         const LLString& contents = node->getValue();
00195 
00196         if (contents.find_first_not_of(" \n\t") != contents.npos)
00197         {
00198                 llerrs << "Legacy combo box item format used! Please convert to <combo_item> tags!" << llendl;
00199         }
00200         else
00201         {
00202                 LLXMLNodePtr child;
00203                 for (child = node->getFirstChild(); child.notNull(); child = child->getNextSibling())
00204                 {
00205                         if (child->hasName("combo_item"))
00206                         {
00207                                 LLString label = child->getTextContents();
00208 
00209                                 LLString value = label;
00210                                 child->getAttributeString("value", value);
00211 
00212                                 combo_box->add(label, LLSD(value) );
00213                         }
00214                 }
00215         }
00216 
00217         combo_box->selectFirstItem();
00218 
00219         return combo_box;
00220 }
00221 
00222 void LLComboBox::setEnabled(BOOL enabled)
00223 {
00224         LLView::setEnabled(enabled);
00225         mButton->setEnabled(enabled);
00226 }
00227 
00228 void LLComboBox::clear()
00229 { 
00230         if (mTextEntry)
00231         {
00232                 mTextEntry->setText(LLString::null);
00233         }
00234         mButton->setLabelSelected(LLString::null);
00235         mButton->setLabelUnselected(LLString::null);
00236         mButton->setDisabledLabel(LLString::null);
00237         mButton->setDisabledSelectedLabel(LLString::null);
00238         mList->deselectAllItems();
00239 }
00240 
00241 void LLComboBox::onCommit()
00242 {
00243         if (mAllowTextEntry && getCurrentIndex() != -1)
00244         {
00245                 // we have selected an existing item, blitz the manual text entry with
00246                 // the properly capitalized item
00247                 mTextEntry->setValue(getSimple());
00248                 mTextEntry->setTentative(FALSE);
00249         }
00250         LLUICtrl::onCommit();
00251 }
00252 
00253 // virtual
00254 BOOL LLComboBox::isDirty() const
00255 {
00256         BOOL grubby = FALSE;
00257         if ( mList )
00258         {
00259                 grubby = mList->isDirty();
00260         }
00261         return grubby;
00262 }
00263 
00264 // virtual   Clear dirty state
00265 void    LLComboBox::resetDirty()
00266 {
00267         if ( mList )
00268         {
00269                 mList->resetDirty();
00270         }
00271 }
00272 
00273 
00274 // add item "name" to menu
00275 void LLComboBox::add(const LLString& name, EAddPosition pos, BOOL enabled)
00276 {
00277         mList->addSimpleItem(name, pos, enabled);
00278         mList->selectFirstItem();
00279 }
00280 
00281 // add item "name" with a unique id to menu
00282 void LLComboBox::add(const LLString& name, const LLUUID& id, EAddPosition pos, BOOL enabled )
00283 {
00284         mList->addSimpleItem(name, LLSD(id), pos, enabled);
00285         mList->selectFirstItem();
00286 }
00287 
00288 // add item "name" with attached userdata
00289 void LLComboBox::add(const LLString& name, void* userdata, EAddPosition pos, BOOL enabled )
00290 {
00291         LLScrollListItem* item = mList->addSimpleItem(name, pos, enabled);
00292         item->setUserdata( userdata );
00293         mList->selectFirstItem();
00294 }
00295 
00296 // add item "name" with attached generic data
00297 void LLComboBox::add(const LLString& name, LLSD value, EAddPosition pos, BOOL enabled )
00298 {
00299         mList->addSimpleItem(name, value, pos, enabled);
00300         mList->selectFirstItem();
00301 }
00302 
00303 
00304 void LLComboBox::sortByName()
00305 {
00306         mList->sortByColumn(0, TRUE);
00307 }
00308 
00309 
00310 // Choose an item with a given name in the menu.
00311 // Returns TRUE if the item was found.
00312 BOOL LLComboBox::setSimple(const LLStringExplicit& name)
00313 {
00314         BOOL found = mList->selectSimpleItem(name, FALSE);
00315 
00316         if (found)
00317         {
00318                 setLabel(name);
00319         }
00320 
00321         return found;
00322 }
00323 
00324 // virtual
00325 void LLComboBox::setValue(const LLSD& value)
00326 {
00327         BOOL found = mList->selectByValue(value);
00328         if (found)
00329         {
00330                 LLScrollListItem* item = mList->getFirstSelected();
00331                 if (item)
00332                 {
00333                         setLabel( mList->getSimpleSelectedItem() );
00334                 }
00335         }
00336 }
00337 
00338 const LLString& LLComboBox::getSimple() const
00339 {
00340         const LLString& res = mList->getSimpleSelectedItem();
00341         if (res.empty() && mAllowTextEntry)
00342         {
00343                 return mTextEntry->getText();
00344         }
00345         else
00346         {
00347                 return res;
00348         }
00349 }
00350 
00351 const LLString& LLComboBox::getSimpleSelectedItem(S32 column) const
00352 {
00353         return mList->getSimpleSelectedItem(column);
00354 }
00355 
00356 // virtual
00357 LLSD LLComboBox::getValue() const
00358 {
00359         LLScrollListItem* item = mList->getFirstSelected();
00360         if( item )
00361         {
00362                 return item->getValue();
00363         }
00364         else if (mAllowTextEntry)
00365         {
00366                 return mTextEntry->getValue();
00367         }
00368         else
00369         {
00370                 return LLSD();
00371         }
00372 }
00373 
00374 void LLComboBox::setLabel(const LLStringExplicit& name)
00375 {
00376         if ( mAllowTextEntry )
00377         {
00378                 mTextEntry->setText(name);
00379                 if (mList->selectSimpleItem(name, FALSE))
00380                 {
00381                         mTextEntry->setTentative(FALSE);
00382                 }
00383                 else
00384                 {
00385                         mTextEntry->setTentative(mTextEntryTentative);
00386                 }
00387         }
00388         else
00389         {
00390                 mButton->setLabelUnselected(name);
00391                 mButton->setLabelSelected(name);
00392                 mButton->setDisabledLabel(name);
00393                 mButton->setDisabledSelectedLabel(name);
00394         }
00395 }
00396 
00397 
00398 BOOL LLComboBox::remove(const LLString& name)
00399 {
00400         BOOL found = mList->selectSimpleItem(name);
00401 
00402         if (found)
00403         {
00404                 LLScrollListItem* item = mList->getFirstSelected();
00405                 if (item)
00406                 {
00407                         mList->deleteSingleItem(mList->getItemIndex(item));
00408                 }
00409         }
00410 
00411         return found;
00412 }
00413 
00414 BOOL LLComboBox::remove(S32 index)
00415 {
00416         if (index < mList->getItemCount())
00417         {
00418                 mList->deleteSingleItem(index);
00419                 return TRUE;
00420         }
00421         return FALSE;
00422 }
00423 
00424 // Keyboard focus lost.
00425 void LLComboBox::onFocusLost()
00426 {
00427         hideList();
00428         // if valid selection
00429         if (mAllowTextEntry && getCurrentIndex() != -1)
00430         {
00431                 mTextEntry->selectAll();
00432         }
00433         LLUICtrl::onFocusLost();
00434 }
00435 
00436 void LLComboBox::setButtonVisible(BOOL visible)
00437 {
00438         mButton->setVisible(visible);
00439         mDrawArrow = visible;
00440         if (mTextEntry)
00441         {
00442                 LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
00443                 if (visible)
00444                 {
00445                         text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
00446                 }
00447                 //mTextEntry->setRect(text_entry_rect);
00448                 mTextEntry->reshape(text_entry_rect.getWidth(), text_entry_rect.getHeight(), TRUE);
00449         }
00450 }
00451 
00452 void LLComboBox::draw()
00453 {
00454         if( getVisible() )
00455         {
00456                 mBorder->setKeyboardFocusHighlight(hasFocus());
00457 
00458                 mButton->setEnabled(mEnabled /*&& !mList->isEmpty()*/);
00459 
00460                 // Draw children
00461                 LLUICtrl::draw();
00462 
00463                 if (mDrawArrow)
00464                 {
00465                         // Paste the graphic on the right edge
00466                         if (!mArrowImage.isNull())
00467                         {
00468                                 S32 arrow_height = llmin(mRect.getHeight(), mArrowImage->getHeight());
00469                                 S32 arrow_width = llround((F32)mArrowImage->getWidth() * ((F32)arrow_height / (F32)mArrowImage->getHeight()));
00470 
00471                                 S32 left = mRect.getWidth() - mArrowImage->getWidth() - LLUI::sConfigGroup->getS32("DropShadowButton");
00472 
00473                                 gl_draw_scaled_image( left, 0, arrow_width, arrow_height, mArrowImage, LLColor4::white);
00474                         }
00475                 }
00476         }
00477 }
00478 
00479 BOOL LLComboBox::setCurrentByIndex( S32 index )
00480 {
00481         BOOL found = mList->selectNthItem( index );
00482         if (found)
00483         {
00484                 setLabel(mList->getSimpleSelectedItem());
00485         }
00486         return found;
00487 }
00488 
00489 S32 LLComboBox::getCurrentIndex() const
00490 {
00491         LLScrollListItem* item = mList->getFirstSelected();
00492         if( item )
00493         {
00494                 return mList->getItemIndex( item );
00495         }
00496         return -1;
00497 }
00498 
00499 
00500 void* LLComboBox::getCurrentUserdata()
00501 {
00502         LLScrollListItem* item = mList->getFirstSelected();
00503         if( item )
00504         {
00505                 return item->getUserdata();
00506         }
00507         return NULL;
00508 }
00509 
00510 
00511 void LLComboBox::showList()
00512 {
00513         // Make sure we don't go off top of screen.
00514         LLCoordWindow window_size;
00515         getWindow()->getSize(&window_size);
00516         //HACK: shouldn't have to know about scale here
00517         mList->arrange( 192, llfloor((F32)window_size.mY / LLUI::sGLScaleFactor.mV[VY]) - 50 );
00518 
00519         // Make sure that we can see the whole list
00520         LLRect root_view_local;
00521         LLView* root_view = getRootView();
00522         root_view->localRectToOtherView(root_view->getLocalRect(), &root_view_local, this);
00523         
00524         LLRect rect = mList->getRect();
00525 
00526         S32 list_width = mRect.getWidth() + SCROLLBAR_SIZE;
00527 
00528         if (mListPosition == BELOW)
00529         {
00530                 if (rect.getHeight() <= -root_view_local.mBottom)
00531                 {
00532                         // Move rect so it hangs off the bottom of this view
00533                         rect.setLeftTopAndSize(0, 0, list_width, rect.getHeight() );
00534                 }
00535                 else
00536                 {       
00537                         // stack on top or bottom, depending on which has more room
00538                         if (-root_view_local.mBottom > root_view_local.mTop - mRect.getHeight())
00539                         {
00540                                 // Move rect so it hangs off the bottom of this view
00541                                 rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
00542                         }
00543                         else
00544                         {
00545                                 // move rect so it stacks on top of this view (clipped to size of screen)
00546                                 rect.setOriginAndSize(0, mRect.getHeight(), list_width, llmin(root_view_local.mTop - mRect.getHeight(), rect.getHeight()));
00547                         }
00548                 }
00549         }
00550         else // ABOVE
00551         {
00552                 if (rect.getHeight() <= root_view_local.mTop - mRect.getHeight())
00553                 {
00554                         // move rect so it stacks on top of this view (clipped to size of screen)
00555                         rect.setOriginAndSize(0, mRect.getHeight(), list_width, llmin(root_view_local.mTop - mRect.getHeight(), rect.getHeight()));
00556                 }
00557                 else
00558                 {
00559                         // stack on top or bottom, depending on which has more room
00560                         if (-root_view_local.mBottom > root_view_local.mTop - mRect.getHeight())
00561                         {
00562                                 // Move rect so it hangs off the bottom of this view
00563                                 rect.setLeftTopAndSize(0, 0, list_width, llmin(-root_view_local.mBottom, rect.getHeight()));
00564                         }
00565                         else
00566                         {
00567                                 // move rect so it stacks on top of this view (clipped to size of screen)
00568                                 rect.setOriginAndSize(0, mRect.getHeight(), list_width, llmin(root_view_local.mTop - mRect.getHeight(), rect.getHeight()));
00569                         }
00570                 }
00571 
00572         }
00573         mList->setOrigin(rect.mLeft, rect.mBottom);
00574         mList->reshape(rect.getWidth(), rect.getHeight());
00575         mList->translateIntoRect(root_view_local, FALSE);
00576 
00577         // Make sure we didn't go off bottom of screen
00578         S32 x, y;
00579         mList->localPointToScreen(0, 0, &x, &y);
00580 
00581         if (y < 0)
00582         {
00583                 mList->translate(0, -y);
00584         }
00585 
00586         // pass mouse capture on to list if button is depressed
00587         if (mButton->hasMouseCapture())
00588         {
00589                 gFocusMgr.setMouseCapture(mList);
00590         }
00591         
00592         // NB: this call will trigger the focuslost callback which will hide the list, so do it first
00593         // before finally showing the list
00594 
00595         if (!mList->getFirstSelected())
00596         {
00597                 // if nothing is selected, select the first item
00598                 // so that the callback is not immediately triggered on setFocus()
00599                 mList->selectFirstItem();
00600         }
00601         mList->setFocus(TRUE);
00602 
00603         // Show the list and push the button down
00604         mButton->setToggleState(TRUE);
00605         mList->setVisible(TRUE);
00606         
00607         gFocusMgr.setTopCtrl(mList);
00608 }
00609 
00610 void LLComboBox::hideList()
00611 {
00612         mButton->setToggleState(FALSE);
00613         mList->setVisible(FALSE);
00614         mList->highlightNthItem(-1);
00615 
00616         if( gFocusMgr.getTopCtrl() == mList )
00617         {
00618                 gFocusMgr.setTopCtrl(NULL);
00619         }
00620 
00621         //mList->setFocus(FALSE);
00622 }
00623 
00624 
00625 //------------------------------------------------------------------
00626 // static functions
00627 //------------------------------------------------------------------
00628 
00629 // static
00630 void LLComboBox::onButtonDown(void *userdata)
00631 {
00632         LLComboBox *self = (LLComboBox *)userdata;
00633 
00634         if (!self->mList->getVisible())
00635         {
00636                 LLScrollListItem* last_selected_item = self->mList->getLastSelectedItem();
00637                 if (last_selected_item)
00638                 {
00639                         // highlight the original selection before potentially selecting a new item
00640                         self->mList->highlightNthItem(self->mList->getItemIndex(last_selected_item));
00641                 }
00642 
00643                 if( self->mPrearrangeCallback )
00644                 {
00645                         self->mPrearrangeCallback( self, self->mCallbackUserData );
00646                 }
00647 
00648                 if (self->mList->getItemCount() != 0)
00649                 {
00650                         self->showList();
00651                 }
00652 
00653                 if (self->mKeyboardFocusOnClick && !self->hasFocus())
00654                 {
00655                         self->setFocus( TRUE );
00656                 }
00657         }
00658         else
00659         {
00660                 // hide and release keyboard focus
00661                 self->hideList();
00662 
00663                 self->onCommit();
00664         }
00665 }
00666 
00667 
00668 
00669 // static
00670 void LLComboBox::onItemSelected(LLUICtrl* item, void *userdata)
00671 {
00672         // Note: item is the LLScrollListCtrl
00673         LLComboBox *self = (LLComboBox *) userdata;
00674 
00675         const LLString& name = self->mList->getSimpleSelectedItem();
00676 
00677         S32 cur_id = self->getCurrentIndex();
00678         if (cur_id != -1)
00679         {
00680                 self->setLabel(name);
00681 
00682                 if (self->mAllowTextEntry)
00683                 {
00684                         gFocusMgr.setKeyboardFocus(self->mTextEntry, NULL);
00685                         self->mTextEntry->selectAll();
00686                 }
00687         }
00688         else
00689         {
00690                 // invalid selection, just restore existing value
00691                 self->mList->selectSimpleItem(self->mButton->getLabelSelected());
00692         }
00693         self->onCommit();
00694 
00695         self->hideList();
00696 }
00697 
00698 // static
00699 void LLComboBox::onListFocusChanged(LLUICtrl* list, void* user_data)
00700 {
00701         LLComboBox *self = (LLComboBox *) list->getParent();
00702         // user not manipulating list or clicking on drop down button
00703         if (!self->mList->hasFocus() && !self->mButton->hasMouseCapture())
00704         {
00705                 //*HACK: store the original value explicitly somewhere, not just in label
00706                 LLString orig_selection = self->mAllowTextEntry ? self->mTextEntry->getText() : self->mButton->getLabelSelected();
00707 
00708                 self->hideList();
00709 
00710                 // reassert original selection
00711                 self->mList->selectSimpleItem(orig_selection, FALSE);
00712         }
00713 }
00714 
00715 BOOL LLComboBox::handleToolTip(S32 x, S32 y, LLString& msg, LLRect* sticky_rect_screen)
00716 {
00717 
00718     LLString tool_tip;
00719 
00720         if (LLUI::sShowXUINames)
00721         {
00722                 tool_tip = getShowNamesToolTip();
00723         }
00724         else
00725         {
00726                 tool_tip = mToolTipMsg;
00727         }
00728 
00729         if( getVisible() && pointInView( x, y ) ) 
00730         {
00731                 if( !tool_tip.empty() )
00732                 {
00733                         msg = tool_tip;
00734 
00735                         // Convert rect local to screen coordinates
00736                         localPointToScreen( 
00737                                 0, 0, 
00738                                 &(sticky_rect_screen->mLeft), &(sticky_rect_screen->mBottom) );
00739                         localPointToScreen(
00740                                 mRect.getWidth(), mRect.getHeight(),
00741                                 &(sticky_rect_screen->mRight), &(sticky_rect_screen->mTop) );
00742                 }
00743                 return TRUE;
00744         }
00745         return FALSE;
00746 }
00747 
00748 BOOL LLComboBox::handleKeyHere(KEY key, MASK mask, BOOL called_from_parent)
00749 {
00750         BOOL result = FALSE;
00751         if (gFocusMgr.childHasKeyboardFocus(this))
00752         {
00753                 //give list a chance to pop up and handle key
00754                 LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
00755                 if (last_selected_item)
00756                 {
00757                         // highlight the original selection before potentially selecting a new item
00758                         mList->highlightNthItem(mList->getItemIndex(last_selected_item));
00759                 }
00760                 result = mList->handleKeyHere(key, mask, FALSE);
00761                 // if selection has changed, pop open list
00762                 if (mList->getLastSelectedItem() != last_selected_item)
00763                 {
00764                         showList();
00765                 }
00766         }
00767         return result;
00768 }
00769 
00770 BOOL LLComboBox::handleUnicodeCharHere(llwchar uni_char, BOOL called_from_parent)
00771 {
00772         BOOL result = FALSE;
00773         if (gFocusMgr.childHasKeyboardFocus(this))
00774         {
00775                 // space bar just shows the list
00776                 if (' ' != uni_char )
00777                 {
00778                         LLScrollListItem* last_selected_item = mList->getLastSelectedItem();
00779                         if (last_selected_item)
00780                         {
00781                                 // highlight the original selection before potentially selecting a new item
00782                                 mList->highlightNthItem(mList->getItemIndex(last_selected_item));
00783                         }
00784                         result = mList->handleUnicodeCharHere(uni_char, called_from_parent);
00785                         if (mList->getLastSelectedItem() != last_selected_item)
00786                         {
00787                                 showList();
00788                         }
00789                 }
00790         }
00791         return result;
00792 }
00793 
00794 void LLComboBox::setAllowTextEntry(BOOL allow, S32 max_chars, BOOL set_tentative)
00795 {
00796         LLRect rect( 0, mRect.getHeight(), mRect.getWidth(), 0);
00797         if (allow && !mAllowTextEntry)
00798         {
00799                 S32 shadow_size = LLUI::sConfigGroup->getS32("DropShadowButton");
00800                 mButton->setRect(LLRect( mRect.getWidth() - mArrowImageWidth - 2 * shadow_size,
00801                                                                 rect.mTop, rect.mRight, rect.mBottom));
00802                 mButton->setTabStop(FALSE);
00803 
00804                 // clear label on button
00805                 LLString cur_label = mButton->getLabelSelected();
00806                 setLabel(LLString::null);
00807                 if (!mTextEntry)
00808                 {
00809                         LLRect text_entry_rect(0, mRect.getHeight(), mRect.getWidth(), 0);
00810                         text_entry_rect.mRight -= mArrowImageWidth + 2 * LLUI::sConfigGroup->getS32("DropShadowButton");
00811                         mTextEntry = new LLLineEditor("combo_text_entry",
00812                                                                                 text_entry_rect,
00813                                                                                 "",
00814                                                                                 LLFontGL::sSansSerifSmall,
00815                                                                                 max_chars,
00816                                                                                 onTextCommit,
00817                                                                                 onTextEntry,
00818                                                                                 NULL,
00819                                                                                 this,
00820                                                                                 NULL,           // prevalidate func
00821                                                                                 LLViewBorder::BEVEL_NONE,
00822                                                                                 LLViewBorder::STYLE_LINE,
00823                                                                                 0);     // no border
00824                         mTextEntry->setSelectAllonFocusReceived(TRUE);
00825                         mTextEntry->setHandleEditKeysDirectly(TRUE);
00826                         mTextEntry->setCommitOnFocusLost(FALSE);
00827                         mTextEntry->setText(cur_label);
00828                         mTextEntry->setIgnoreTab(TRUE);
00829                         mTextEntry->setFollowsAll();
00830                         addChild(mTextEntry);
00831                         mMaxChars = max_chars;
00832                 }
00833                 else
00834                 {
00835                         mTextEntry->setVisible(TRUE);
00836                 }
00837 
00838                 mButton->setFollows(FOLLOWS_BOTTOM | FOLLOWS_TOP | FOLLOWS_RIGHT);
00839         }
00840         else if (!allow && mAllowTextEntry)
00841         {
00842                 mButton->setRect(rect);
00843                 mButton->setTabStop(TRUE);
00844 
00845                 if (mTextEntry)
00846                 {
00847                         mTextEntry->setVisible(FALSE);
00848                 }
00849                 mButton->setFollowsAll();
00850         }
00851         mAllowTextEntry = allow;
00852         mTextEntryTentative = set_tentative;    
00853 }
00854 
00855 void LLComboBox::setTextEntry(const LLStringExplicit& text)
00856 {
00857         if (mTextEntry)
00858         {
00859                 mTextEntry->setText(text);
00860                 updateSelection();
00861         }
00862 }
00863 
00864 //static 
00865 void LLComboBox::onTextEntry(LLLineEditor* line_editor, void* user_data)
00866 {
00867         LLComboBox* self = (LLComboBox*)user_data;
00868 
00869         if (self->mTextEntryCallback)
00870         {
00871                 (*self->mTextEntryCallback)(line_editor, self->mCallbackUserData);
00872         }
00873 
00874         KEY key = gKeyboard->currentKey();
00875         if (key == KEY_BACKSPACE || 
00876                 key == KEY_DELETE)
00877         {
00878                 if (self->mList->selectSimpleItem(line_editor->getText(), FALSE))
00879                 {
00880                         line_editor->setTentative(FALSE);
00881                 }
00882                 else
00883                 {
00884                         line_editor->setTentative(self->mTextEntryTentative);
00885                         self->mList->deselectAllItems();
00886                 }
00887                 return;
00888         }
00889 
00890         if (key == KEY_LEFT || 
00891                 key == KEY_RIGHT)
00892         {
00893                 return;
00894         }
00895 
00896         if (key == KEY_DOWN)
00897         {
00898                 self->setCurrentByIndex(llmin(self->getItemCount() - 1, self->getCurrentIndex() + 1));
00899                 if (!self->mList->getVisible())
00900                 {
00901                         if( self->mPrearrangeCallback )
00902                         {
00903                                 self->mPrearrangeCallback( self, self->mCallbackUserData );
00904                         }
00905 
00906                         if (self->mList->getItemCount() != 0)
00907                         {
00908                                 self->showList();
00909                         }
00910                 }
00911                 line_editor->selectAll();
00912                 line_editor->setTentative(FALSE);
00913         }
00914         else if (key == KEY_UP)
00915         {
00916                 self->setCurrentByIndex(llmax(0, self->getCurrentIndex() - 1));
00917                 if (!self->mList->getVisible())
00918                 {
00919                         if( self->mPrearrangeCallback )
00920                         {
00921                                 self->mPrearrangeCallback( self, self->mCallbackUserData );
00922                         }
00923 
00924                         if (self->mList->getItemCount() != 0)
00925                         {
00926                                 self->showList();
00927                         }
00928                 }
00929                 line_editor->selectAll();
00930                 line_editor->setTentative(FALSE);
00931         }
00932         else
00933         {
00934                 // RN: presumably text entry
00935                 self->updateSelection();
00936         }
00937 }
00938 
00939 void LLComboBox::updateSelection()
00940 {
00941         LLWString left_wstring = mTextEntry->getWText().substr(0, mTextEntry->getCursor());
00942         // user-entered portion of string, based on assumption that any selected
00943     // text was a result of auto-completion
00944         LLWString user_wstring = mTextEntry->hasSelection() ? left_wstring : mTextEntry->getWText();
00945         LLString full_string = mTextEntry->getText();
00946 
00947         // go ahead and arrange drop down list on first typed character, even
00948         // though we aren't showing it... some code relies on prearrange
00949         // callback to populate content
00950         if( mTextEntry->getWText().size() == 1 )
00951         {
00952                 if (mPrearrangeCallback)
00953                 {
00954                         mPrearrangeCallback( this, mCallbackUserData );
00955                 }
00956         }
00957 
00958         if (mList->selectSimpleItem(full_string, FALSE))
00959         {
00960                 mTextEntry->setTentative(FALSE);
00961         }
00962         else if (!mList->selectSimpleItemByPrefix(left_wstring, FALSE))
00963         {
00964                 mList->deselectAllItems();
00965                 mTextEntry->setText(wstring_to_utf8str(user_wstring));
00966                 mTextEntry->setTentative(mTextEntryTentative);
00967         }
00968         else
00969         {
00970                 LLWString selected_item = utf8str_to_wstring(mList->getSimpleSelectedItem());
00971                 LLWString wtext = left_wstring + selected_item.substr(left_wstring.size(), selected_item.size());
00972                 mTextEntry->setText(wstring_to_utf8str(wtext));
00973                 mTextEntry->setSelection(left_wstring.size(), mTextEntry->getWText().size());
00974                 mTextEntry->endSelection();
00975                 mTextEntry->setTentative(FALSE);
00976         }
00977 }
00978 
00979 //static 
00980 void LLComboBox::onTextCommit(LLUICtrl* caller, void* user_data)
00981 {
00982         LLComboBox* self = (LLComboBox*)user_data;
00983         LLString text = self->mTextEntry->getText();
00984         self->setSimple(text);
00985         self->onCommit();
00986         self->mTextEntry->selectAll();
00987 }
00988 
00989 void LLComboBox::setFocus(BOOL b)
00990 {
00991         LLUICtrl::setFocus(b);
00992 
00993         if (b)
00994         {
00995                 mList->clearSearchString();
00996         }
00997 }
00998 
00999 //============================================================================
01000 // LLCtrlListInterface functions
01001 
01002 S32 LLComboBox::getItemCount() const
01003 {
01004         return mList->getItemCount();
01005 }
01006 
01007 void LLComboBox::addColumn(const LLSD& column, EAddPosition pos)
01008 {
01009         mList->clearColumns();
01010         mList->addColumn(column, pos);
01011 }
01012 
01013 void LLComboBox::clearColumns()
01014 {
01015         mList->clearColumns();
01016 }
01017 
01018 void LLComboBox::setColumnLabel(const LLString& column, const LLString& label)
01019 {
01020         mList->setColumnLabel(column, label);
01021 }
01022 
01023 LLScrollListItem* LLComboBox::addElement(const LLSD& value, EAddPosition pos, void* userdata)
01024 {
01025         return mList->addElement(value, pos, userdata);
01026 }
01027 
01028 LLScrollListItem* LLComboBox::addSimpleElement(const LLString& value, EAddPosition pos, const LLSD& id)
01029 {
01030         return mList->addSimpleElement(value, pos, id);
01031 }
01032 
01033 void LLComboBox::clearRows()
01034 {
01035         mList->clearRows();
01036 }
01037 
01038 void LLComboBox::sortByColumn(LLString name, BOOL ascending)
01039 {
01040 }
01041 
01042 //============================================================================
01043 //LLCtrlSelectionInterface functions
01044 
01045 BOOL LLComboBox::setCurrentByID(const LLUUID& id)
01046 {
01047         BOOL found = mList->selectByID( id );
01048 
01049         if (found)
01050         {
01051                 setLabel(mList->getSimpleSelectedItem());
01052         }
01053 
01054         return found;
01055 }
01056 
01057 LLUUID LLComboBox::getCurrentID()
01058 {
01059         return mList->getStringUUIDSelectedItem();
01060 }
01061 BOOL LLComboBox::setSelectedByValue(LLSD value, BOOL selected)
01062 {
01063         BOOL found = mList->setSelectedByValue(value, selected);
01064         if (found)
01065         {
01066                 setLabel(mList->getSimpleSelectedItem());
01067         }
01068         return found;
01069 }
01070 
01071 LLSD LLComboBox::getSimpleSelectedValue()
01072 {
01073         return mList->getSimpleSelectedValue();
01074 }
01075 
01076 BOOL LLComboBox::isSelected(LLSD value)
01077 {
01078         return mList->isSelected(value);
01079 }
01080 
01081 BOOL LLComboBox::operateOnSelection(EOperation op)
01082 {
01083         if (op == OP_DELETE)
01084         {
01085                 mList->deleteSelectedItems();
01086                 return TRUE;
01087         }
01088         return FALSE;
01089 }
01090 
01091 BOOL LLComboBox::operateOnAll(EOperation op)
01092 {
01093         if (op == OP_DELETE)
01094         {
01095                 clearRows();
01096                 return TRUE;
01097         }
01098         return FALSE;
01099 }

Generated on Thu Jul 1 06:08:22 2010 for Second Life Viewer by  doxygen 1.4.7