xref: /haiku/src/kits/interface/OptionPopUp.cpp (revision 3bcb69811be906f594939fa43cd8e81018367d1a)
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 */
BOptionPopUp(BRect frame,const char * name,const char * label,BMessage * message,uint32 resize,uint32 flags)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 */
BOptionPopUp(BRect frame,const char * name,const char * label,BMessage * message,bool fixed,uint32 resize,uint32 flags)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 
BOptionPopUp(const char * name,const char * label,BMessage * message,uint32 flags)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 
~BOptionPopUp()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*
MenuField()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
GetOptionAt(int32 index,const char ** outName,int32 * outValue)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
RemoveOptionAt(int32 index)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
CountOptions() const136 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
AddOptionAt(const char * name,int32 value,int32 index)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
AllAttached()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
AttachedToWindow()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
MessageReceived(BMessage * message)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
SetLabel(const char * text)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
SetValue(int32 value)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
SetEnabled(bool state)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
GetPreferredSize(float * _width,float * _height)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
ResizeToPreferred()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
SelectedOption(const char ** outName,int32 * outValue) const324 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
BOptionPopUp()344 BOptionPopUp::BOptionPopUp()
345 	:
346 	BOptionControl(BRect(), "", "", NULL)
347 {
348 }
349 
350 
BOptionPopUp(const BOptionPopUp & clone)351 BOptionPopUp::BOptionPopUp(const BOptionPopUp& clone)
352 	:
353 	BOptionControl(clone.Frame(), "", "", clone.Message())
354 {
355 }
356 
357 
358 BOptionPopUp &
operator =(const BOptionPopUp & clone)359 BOptionPopUp::operator=(const BOptionPopUp& clone)
360 {
361 	return *this;
362 }
363 
364 
365 // FBC Stuff
_Reserved_OptionControl_0(void *,...)366 status_t BOptionPopUp::_Reserved_OptionControl_0(void *, ...) { return B_ERROR; }
_Reserved_OptionControl_1(void *,...)367 status_t BOptionPopUp::_Reserved_OptionControl_1(void *, ...) { return B_ERROR; }
_Reserved_OptionControl_2(void *,...)368 status_t BOptionPopUp::_Reserved_OptionControl_2(void *, ...) { return B_ERROR; }
_Reserved_OptionControl_3(void *,...)369 status_t BOptionPopUp::_Reserved_OptionControl_3(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_0(void *,...)370 status_t BOptionPopUp::_Reserved_OptionPopUp_0(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_1(void *,...)371 status_t BOptionPopUp::_Reserved_OptionPopUp_1(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_2(void *,...)372 status_t BOptionPopUp::_Reserved_OptionPopUp_2(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_3(void *,...)373 status_t BOptionPopUp::_Reserved_OptionPopUp_3(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_4(void *,...)374 status_t BOptionPopUp::_Reserved_OptionPopUp_4(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_5(void *,...)375 status_t BOptionPopUp::_Reserved_OptionPopUp_5(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_6(void *,...)376 status_t BOptionPopUp::_Reserved_OptionPopUp_6(void *, ...) { return B_ERROR; }
_Reserved_OptionPopUp_7(void *,...)377 status_t BOptionPopUp::_Reserved_OptionPopUp_7(void *, ...) { return B_ERROR; }
378