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