1 //----------------------------------------------------------------------------- 2 // Copyright (c) 2003 Stefano Ceccherini 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: OptionPopUp.cpp 23 // Description: An option like control. 24 //------------------------------------------------------------------------------ 25 #include <MenuField.h> 26 #include <MenuItem.h> 27 #include <OptionPopUp.h> 28 #include <PopUpMenu.h> 29 30 #include <cstdio> 31 32 // If enabled, behaves like in BeOS R5, in that when you call 33 // SelectOptionFor() or SetValue(), the selected item isn't marked, and 34 // so SelectedOption() will return -1. This is broken, IMHO. 35 #define BEHAVE_LIKE_R5 0 36 37 const float kLabelSpace = 8.0; 38 const float kWidthModifier = 25.0; 39 const float kHeightModifier = 10.0; 40 41 42 /*! \brief Creates and initializes a BOptionPopUp. 43 \param frame The frame of the control. 44 \param name The name of the control. 45 \param label The label which will be displayed by the control. 46 \param message The message which the control will send when operated. 47 \param resize Resizing flags. They will be passed to the base class. 48 \param flags View flags. They will be passed to the base class. 49 */ 50 BOptionPopUp::BOptionPopUp(BRect frame, const char *name, const char *label, 51 BMessage *message, uint32 resize, uint32 flags) 52 : 53 BOptionControl(frame, name, label, message, resize, flags) 54 { 55 BPopUpMenu *popUp = new BPopUpMenu(label, true, true); 56 _mField = new BMenuField(Bounds(), "_menu", label, popUp); 57 AddChild(_mField); 58 } 59 60 61 /*! \brief Creates and initializes a BOptionPopUp. 62 \param frame The frame of the control. 63 \param name The name of the control. 64 \param label The label which will be displayed by the control. 65 \param message The message which the control will send when operated. 66 \param fixed It's passed to the BMenuField constructor. If it's true, 67 the BMenuField size will never change. 68 \param resize Resizing flags. They will be passed to the base class. 69 \param flags View flags. They will be passed to the base class. 70 */ 71 BOptionPopUp::BOptionPopUp(BRect frame, const char *name, const char *label, 72 BMessage *message, bool fixed, uint32 resize, uint32 flags) 73 : 74 BOptionControl(frame, name, label, message, resize, flags) 75 { 76 BPopUpMenu *popUp = new BPopUpMenu(label, true, true); 77 _mField = new BMenuField(Bounds(), "_menu", label, popUp, fixed); 78 AddChild(_mField); 79 } 80 81 82 /*! \brief Frees the allocated resources. 83 It does nothing. 84 */ 85 BOptionPopUp::~BOptionPopUp() 86 { 87 } 88 89 90 /*! \brief Returns a pointer to the BMenuField used internally. 91 \return A Pointer to the BMenuField which the class uses internally. 92 */ 93 BMenuField * 94 BOptionPopUp::MenuField() 95 { 96 return _mField; 97 } 98 99 100 /*! \brief Gets the option at the given index. 101 \param index The option's index. 102 \param outName A pointer to a string which will held the option's name, 103 as soon as the function returns. 104 \param outValue A pointer to an integer which will held the option's value, 105 as soon as the funciton returns. 106 \return \c true if The wanted option was found, 107 \c false otherwise. 108 */ 109 bool 110 BOptionPopUp::GetOptionAt(int32 index, const char **outName, int32 *outValue) 111 { 112 bool result = false; 113 BMenu *menu = _mField->Menu(); 114 115 if (menu != NULL) { 116 BMenuItem *item = menu->ItemAt(index); 117 if (item != NULL) { 118 if (outName != NULL) 119 *outName = item->Label(); 120 if (outValue != NULL) 121 item->Message()->FindInt32("be:value", outValue); 122 123 result = true; 124 } 125 } 126 return result; 127 } 128 129 130 /*! \brief Removes the option at the given index. 131 \param index The index of the option to remove. 132 */ 133 void 134 BOptionPopUp::RemoveOptionAt(int32 index) 135 { 136 BMenu *menu = _mField->Menu(); 137 if (menu != NULL) { 138 BMenuItem *item = menu->ItemAt(index); 139 if (item != NULL) { 140 menu->RemoveItem(item); 141 delete item; 142 } 143 } 144 } 145 146 147 /*! \brief Returns the amount of "Options" (entries) contained in the control. 148 */ 149 int32 150 BOptionPopUp::CountOptions() const 151 { 152 BMenu *menu = _mField->Menu(); 153 return (menu != NULL) ? menu->CountItems() : 0; 154 } 155 156 157 /*! \brief Adds an option to the control, at the given position. 158 \param name The name of the option to add. 159 \param value The value of the option. 160 \param index The index which the new option will have in the control. 161 \return \c B_OK if the option was added succesfully, 162 \c B_BAD_VALUE if the given index was invalid. 163 \c B_ERROR if something else happened. 164 */ 165 status_t 166 BOptionPopUp::AddOptionAt(const char *name, int32 value, int32 index) 167 { 168 BMenu *menu = _mField->Menu(); 169 if (menu != NULL) { 170 int32 numItems = menu->CountItems(); 171 if (index < 0 || index > numItems) 172 return B_BAD_VALUE; 173 174 BMessage *message = MakeValueMessage(value); 175 if (message == NULL) 176 return B_ERROR; 177 178 BMenuItem *newItem = new BMenuItem(name, message); 179 menu->AddItem(newItem, index); 180 181 // We didnt' have any items before, so select the newly added one 182 if (numItems == 0) 183 SetValue(value); 184 return B_OK; 185 } 186 187 return B_ERROR; 188 } 189 190 191 /*! \brief Called to take special actions when the child views are attached. 192 It's used to set correctly the divider for the BMenuField. 193 */ 194 void 195 BOptionPopUp::AllAttached() 196 { 197 BMenu *menu = _mField->Menu(); 198 if (menu != NULL) { 199 float labelWidth = _mField->StringWidth(_mField->Label()); 200 _mField->SetDivider(labelWidth + kLabelSpace); 201 } 202 } 203 204 205 void 206 BOptionPopUp::MessageReceived(BMessage *message) 207 { 208 BOptionControl::MessageReceived(message); 209 } 210 211 212 /*! \brief Set the label of the control. 213 \param text The new label of the control. 214 */ 215 void 216 BOptionPopUp::SetLabel(const char *text) 217 { 218 BControl::SetLabel(text); 219 _mField->SetLabel(text); 220 // We are not sure the menu can keep the whole 221 // string as label, so we ask it what label it's got 222 float newWidth = _mField->StringWidth(_mField->Label()); 223 _mField->SetDivider(newWidth + kLabelSpace); 224 } 225 226 227 /*! \brief Set the control's value. 228 \param value The new value of the control. 229 Selects the option which has the given value. 230 */ 231 void 232 BOptionPopUp::SetValue(int32 value) 233 { 234 BControl::SetValue(value); 235 BMenu *menu = _mField->Menu(); 236 int32 numItems = menu->CountItems(); 237 for (int32 i = 0; i < numItems; i++) { 238 BMenuItem *item = menu->ItemAt(i); 239 if (item && item->Message()) { 240 int32 val; 241 item->Message()->FindInt32("be:value", &val); 242 if (val == value) { 243 item->SetMarked(true); 244 245 #if BEHAVE_LIKE_R5 246 item->SetMarked(false); 247 #endif 248 249 break; 250 } 251 } 252 } 253 } 254 255 256 /*! \brief Enables or disables the control. 257 \param state The new control's state. 258 */ 259 void 260 BOptionPopUp::SetEnabled(bool state) 261 { 262 BOptionControl::SetEnabled(state); 263 } 264 265 266 /*! \brief Gets the preferred size for the control. 267 \param width A pointer to a float which will held the control's 268 preferred width. 269 \param height A pointer to a float which will held the control's 270 preferred height. 271 */ 272 void 273 BOptionPopUp::GetPreferredSize(float *width, float *height) 274 { 275 font_height fontHeight; 276 _mField->GetFontHeight(&fontHeight); 277 278 if (height != NULL) 279 *height = fontHeight.ascent + fontHeight.descent + 280 fontHeight.leading + kHeightModifier; 281 282 float maxWidth = 0; 283 BMenu *menu = _mField->Menu(); 284 if (menu == NULL) 285 return; 286 287 int32 numItems = menu->CountItems(); 288 for (int32 i = 0; i < numItems; i++) { 289 BMenuItem *item = menu->ItemAt(i); 290 if (item != NULL) { 291 float stringWidth = menu->StringWidth(item->Label()); 292 maxWidth = max_c(maxWidth, stringWidth); 293 } 294 } 295 296 maxWidth += _mField->StringWidth(BControl::Label()) + kLabelSpace + kWidthModifier; 297 if (width != NULL) 298 *width = maxWidth; 299 } 300 301 302 /*! \brief Resizes the control to its preferred size. 303 */ 304 void 305 BOptionPopUp::ResizeToPreferred() 306 { 307 // TODO: Some more work is needed either here or in GetPreferredSize(), 308 // since the control doesnt' always resize as it should. 309 float width, height; 310 GetPreferredSize(&width, &height); 311 ResizeTo(width, height); 312 313 float newWidth = _mField->StringWidth(BControl::Label()); 314 _mField->SetDivider(newWidth + kLabelSpace); 315 } 316 317 318 /*! \brief Gets the currently selected option. 319 \param outName A pointer to a string which will held the option's name. 320 \param outValue A pointer to an integer which will held the option's value. 321 \return The index of the selected option. 322 */ 323 int32 324 BOptionPopUp::SelectedOption(const char **outName, int32 *outValue) const 325 { 326 BMenu *menu = _mField->Menu(); 327 if (menu != NULL) { 328 BMenuItem *marked = menu->FindMarked(); 329 if (marked != NULL) { 330 if (outName != NULL) 331 *outName = marked->Label(); 332 if (outValue != NULL) 333 *outValue = marked->Message()->FindInt32("be:value", outValue); 334 335 return menu->IndexOf(marked); 336 } 337 } 338 339 return B_ERROR; 340 } 341 342 343 // Private Unimplemented 344 BOptionPopUp::BOptionPopUp() 345 : 346 BOptionControl(BRect(), "", "", NULL) 347 { 348 } 349 350 351 BOptionPopUp::BOptionPopUp(const BOptionPopUp &clone) 352 : 353 BOptionControl(clone.Frame(), "", "", clone.Message()) 354 { 355 } 356 357 358 BOptionPopUp & 359 BOptionPopUp::operator=(const BOptionPopUp & clone) 360 { 361 return *this; 362 } 363 364 365 // FBC Stuff 366 status_t BOptionPopUp::_Reserved_OptionControl_0(void *, ...) { return B_ERROR; } 367 status_t BOptionPopUp::_Reserved_OptionControl_1(void *, ...) { return B_ERROR; } 368 status_t BOptionPopUp::_Reserved_OptionControl_2(void *, ...) { return B_ERROR; } 369 status_t BOptionPopUp::_Reserved_OptionControl_3(void *, ...) { return B_ERROR; } 370 status_t BOptionPopUp::_Reserved_OptionPopUp_0(void *, ...) { return B_ERROR; } 371 status_t BOptionPopUp::_Reserved_OptionPopUp_1(void *, ...) { return B_ERROR; } 372 status_t BOptionPopUp::_Reserved_OptionPopUp_2(void *, ...) { return B_ERROR; } 373 status_t BOptionPopUp::_Reserved_OptionPopUp_3(void *, ...) { return B_ERROR; } 374 status_t BOptionPopUp::_Reserved_OptionPopUp_4(void *, ...) { return B_ERROR; } 375 status_t BOptionPopUp::_Reserved_OptionPopUp_5(void *, ...) { return B_ERROR; } 376 status_t BOptionPopUp::_Reserved_OptionPopUp_6(void *, ...) { return B_ERROR; } 377 status_t BOptionPopUp::_Reserved_OptionPopUp_7(void *, ...) { return B_ERROR; } 378