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 && item->Message() != 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 // BeOS R5 compatibility, do not remove 188 void 189 BOptionPopUp::AllAttached() 190 { 191 BOptionControl::AllAttached(); 192 } 193 194 195 /*! \brief Sets the divider for the BMenuField and target the menu items to ourselves. 196 */ 197 void 198 BOptionPopUp::AttachedToWindow() 199 { 200 BOptionControl::AttachedToWindow(); 201 202 BMenu* menu = fMenuField->Menu(); 203 if (menu != NULL) { 204 float labelWidth = fMenuField->StringWidth(fMenuField->Label()); 205 if (labelWidth > 0.f) 206 labelWidth += kLabelSpace; 207 fMenuField->SetDivider(labelWidth); 208 menu->SetTargetForItems(this); 209 } 210 } 211 212 213 void 214 BOptionPopUp::MessageReceived(BMessage* message) 215 { 216 BOptionControl::MessageReceived(message); 217 } 218 219 220 /*! \brief Set the label of the control. 221 \param text The new label of the control. 222 */ 223 void 224 BOptionPopUp::SetLabel(const char* text) 225 { 226 BControl::SetLabel(text); 227 fMenuField->SetLabel(text); 228 // We are not sure the menu can keep the whole 229 // string as label, so we check against the current label 230 float newWidth = fMenuField->StringWidth(fMenuField->Label()); 231 if (newWidth > 0.f) 232 newWidth += kLabelSpace; 233 fMenuField->SetDivider(newWidth); 234 } 235 236 237 /*! \brief Set the control's value. 238 \param value The new value of the control. 239 Selects the option which has the given value. 240 */ 241 void 242 BOptionPopUp::SetValue(int32 value) 243 { 244 BControl::SetValue(value); 245 BMenu* menu = fMenuField->Menu(); 246 if (menu == NULL) 247 return; 248 249 int32 numItems = menu->CountItems(); 250 for (int32 i = 0; i < numItems; i++) { 251 BMenuItem* item = menu->ItemAt(i); 252 if (item && item->Message()) { 253 int32 itemValue; 254 item->Message()->FindInt32("be:value", &itemValue); 255 if (itemValue == value) { 256 item->SetMarked(true); 257 break; 258 } 259 } 260 } 261 } 262 263 264 /*! \brief Enables or disables the control. 265 \param state The new control's state. 266 */ 267 void 268 BOptionPopUp::SetEnabled(bool state) 269 { 270 BOptionControl::SetEnabled(state); 271 if (fMenuField) 272 fMenuField->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 float width, height; 286 fMenuField->GetPreferredSize(&width, &height); 287 288 if (_height != NULL) { 289 font_height fontHeight; 290 GetFontHeight(&fontHeight); 291 292 *_height = max_c(height, fontHeight.ascent + fontHeight.descent 293 + fontHeight.leading + kHeightModifier); 294 } 295 296 if (_width != NULL) { 297 width += fMenuField->StringWidth(BControl::Label()) 298 + kLabelSpace + kWidthModifier; 299 *_width = width; 300 } 301 } 302 303 304 /*! \brief Resizes the control to its preferred size. 305 */ 306 void 307 BOptionPopUp::ResizeToPreferred() 308 { 309 float width, height; 310 GetPreferredSize(&width, &height); 311 ResizeTo(width, height); 312 313 float newWidth = fMenuField->StringWidth(BControl::Label()); 314 fMenuField->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 = fMenuField->Menu(); 327 if (menu == NULL) 328 return B_ERROR; 329 330 BMenuItem* marked = menu->FindMarked(); 331 if (marked == NULL) 332 return -1; 333 334 if (outName != NULL) 335 *outName = marked->Label(); 336 if (outValue != NULL) 337 marked->Message()->FindInt32("be:value", outValue); 338 339 return menu->IndexOf(marked); 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