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