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