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