xref: /haiku/src/kits/interface/OptionPopUp.cpp (revision 3be9edf8da228afd9fec0390f408c964766122aa)
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