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