xref: /haiku/src/kits/interface/OptionPopUp.cpp (revision aa94570a34695672df9b47adda2257f75d8da880)
1 //-----------------------------------------------------------------------------
2 //	Copyright (c) 2003 Stefano Ceccherini
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		OptionPopUp.cpp
23 //	Description:	An option like control.
24 //------------------------------------------------------------------------------
25 #include <MenuField.h>
26 #include <MenuItem.h>
27 #include <OptionPopUp.h>
28 #include <PopUpMenu.h>
29 
30 #include <cstdio>
31 
32 // If enabled, behaves like in BeOS R5, in that when you call
33 // SelectOptionFor() or SetValue(), the selected item isn't marked, and
34 // so SelectedOption() will return -1. This is broken, IMHO.
35 #define BEHAVE_LIKE_R5 0
36 
37 const float kLabelSpace = 8.0;
38 const float kWidthModifier = 25.0;
39 const float kHeightModifier = 10.0;
40 
41 
42 /*! \brief Creates and initializes a BOptionPopUp.
43 	\param frame The frame of the control.
44 	\param name The name of the control.
45 	\param label The label which will be displayed by the control.
46 	\param message The message which the control will send when operated.
47 	\param resize Resizing flags. They will be passed to the base class.
48 	\param flags View flags. They will be passed to the base class.
49 */
50 BOptionPopUp::BOptionPopUp(BRect frame, const char *name, const char *label,
51 							BMessage *message, uint32 resize, uint32 flags)
52 	:
53 	BOptionControl(frame, name, label, message, resize, flags)
54 {
55 	BPopUpMenu *popUp = new BPopUpMenu(label, true, true);
56 	_mField = new BMenuField(Bounds(), "_menu", label, popUp);
57 	AddChild(_mField);
58 }
59 
60 
61 /*! \brief Creates and initializes a BOptionPopUp.
62 	\param frame The frame of the control.
63 	\param name The name of the control.
64 	\param label The label which will be displayed by the control.
65 	\param message The message which the control will send when operated.
66 	\param fixed It's passed to the BMenuField constructor. If it's true,
67 		the BMenuField size will never change.
68 	\param resize Resizing flags. They will be passed to the base class.
69 	\param flags View flags. They will be passed to the base class.
70 */
71 BOptionPopUp::BOptionPopUp(BRect frame, const char *name, const char *label,
72 						   BMessage *message, bool fixed, uint32 resize, uint32 flags)
73 	:
74 	BOptionControl(frame, name, label, message, resize, flags)
75 {
76 	BPopUpMenu *popUp = new BPopUpMenu(label, true, true);
77 	_mField = new BMenuField(Bounds(), "_menu", label, popUp, fixed);
78 	AddChild(_mField);
79 }
80 
81 
82 /*! \brief Frees the allocated resources.
83 	It does nothing.
84 */
85 BOptionPopUp::~BOptionPopUp()
86 {
87 }
88 
89 
90 /*! \brief Returns a pointer to the BMenuField used internally.
91 	\return A Pointer to the BMenuField which the class uses internally.
92 */
93 BMenuField *
94 BOptionPopUp::MenuField()
95 {
96 	return _mField;
97 }
98 
99 
100 /*! \brief Gets the option at the given index.
101 	\param index The option's index.
102 	\param outName A pointer to a string which will held the option's name,
103 		as soon as the function returns.
104 	\param outValue A pointer to an integer which will held the option's value,
105 		as soon as the funciton returns.
106 	\return \c true if The wanted option was found,
107 			\c false otherwise.
108 */
109 bool
110 BOptionPopUp::GetOptionAt(int32 index, const char **outName, int32 *outValue)
111 {
112 	bool result = false;
113 	BMenu *menu = _mField->Menu();
114 
115 	if (menu != NULL) {
116 		BMenuItem *item = menu->ItemAt(index);
117 		if (item != NULL) {
118 			if (outName != NULL)
119 				*outName = item->Label();
120 			if (outValue != NULL)
121 				item->Message()->FindInt32("be:value", outValue);
122 
123 			result = true;
124 		}
125 	}
126 	return result;
127 }
128 
129 
130 /*! \brief Removes the option at the given index.
131 	\param index The index of the option to remove.
132 */
133 void
134 BOptionPopUp::RemoveOptionAt(int32 index)
135 {
136 	BMenu *menu = _mField->Menu();
137 	if (menu != NULL) {
138 		BMenuItem *item = menu->ItemAt(index);
139 		if (item != NULL) {
140 			menu->RemoveItem(item);
141 			delete item;
142 		}
143 	}
144 }
145 
146 
147 /*! \brief Returns the amount of "Options" (entries) contained in the control.
148 */
149 int32
150 BOptionPopUp::CountOptions() const
151 {
152 	BMenu *menu = _mField->Menu();
153 	return (menu != NULL) ? menu->CountItems() : 0;
154 }
155 
156 
157 /*! \brief Adds an option to the control, at the given position.
158 	\param name The name of the option to add.
159 	\param value The value of the option.
160 	\param index The index which the new option will have in the control.
161 	\return \c B_OK if the option was added succesfully,
162 		\c B_BAD_VALUE if the given index was invalid.
163 		\c B_ERROR if something else happened.
164 */
165 status_t
166 BOptionPopUp::AddOptionAt(const char *name, int32 value, int32 index)
167 {
168 	BMenu *menu = _mField->Menu();
169 	if (menu != NULL) {
170 		int32 numItems = menu->CountItems();
171 		if (index < 0 || index > numItems)
172 			return B_BAD_VALUE;
173 
174 		BMessage *message = MakeValueMessage(value);
175 		if (message == NULL)
176 			return B_ERROR;
177 
178 		BMenuItem *newItem = new BMenuItem(name, message);
179 		menu->AddItem(newItem, index);
180 
181 		// We didnt' have any items before, so select the newly added one
182 		if (numItems == 0)
183 			SetValue(value);
184 		return B_OK;
185 	}
186 
187 	return B_ERROR;
188 }
189 
190 
191 /*! \brief Called to take special actions when the child views are attached.
192 	It's used to set correctly the divider for the BMenuField.
193 */
194 void
195 BOptionPopUp::AllAttached()
196 {
197 	BMenu *menu = _mField->Menu();
198 	if (menu != NULL) {
199 		float labelWidth = _mField->StringWidth(_mField->Label());
200 		_mField->SetDivider(labelWidth + kLabelSpace);
201 	}
202 }
203 
204 
205 void
206 BOptionPopUp::MessageReceived(BMessage *message)
207 {
208 	BOptionControl::MessageReceived(message);
209 }
210 
211 
212 /*! \brief Set the label of the control.
213 	\param text The new label of the control.
214 */
215 void
216 BOptionPopUp::SetLabel(const char *text)
217 {
218 	BControl::SetLabel(text);
219 	_mField->SetLabel(text);
220 	// We are not sure the menu can keep the whole
221 	// string as label, so we ask it what label it's got
222 	float newWidth = _mField->StringWidth(_mField->Label());
223 	_mField->SetDivider(newWidth + kLabelSpace);
224 }
225 
226 
227 /*! \brief Set the control's value.
228 	\param value The new value of the control.
229 	Selects the option which has the given value.
230 */
231 void
232 BOptionPopUp::SetValue(int32 value)
233 {
234 	BControl::SetValue(value);
235 	BMenu *menu = _mField->Menu();
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 val;
241 			item->Message()->FindInt32("be:value", &val);
242 			if (val == value) {
243 				item->SetMarked(true);
244 
245 #if BEHAVE_LIKE_R5
246 				item->SetMarked(false);
247 #endif
248 
249 				break;
250 			}
251 		}
252 	}
253 }
254 
255 
256 /*! \brief Enables or disables the control.
257 	\param state The new control's state.
258 */
259 void
260 BOptionPopUp::SetEnabled(bool state)
261 {
262 	BOptionControl::SetEnabled(state);
263 }
264 
265 
266 /*! \brief Gets the preferred size for the control.
267 	\param width A pointer to a float which will held the control's
268 		preferred width.
269 	\param height A pointer to a float which will held the control's
270 		preferred height.
271 */
272 void
273 BOptionPopUp::GetPreferredSize(float *width, float *height)
274 {
275 	font_height fontHeight;
276 	_mField->GetFontHeight(&fontHeight);
277 
278 	if (height != NULL)
279 		*height = fontHeight.ascent + fontHeight.descent +
280 					fontHeight.leading + kHeightModifier;
281 
282 	float maxWidth = 0;
283 	BMenu *menu = _mField->Menu();
284 	if (menu == NULL)
285 		return;
286 
287 	int32 numItems = menu->CountItems();
288 	for (int32 i = 0; i < numItems; i++) {
289 		BMenuItem *item = menu->ItemAt(i);
290 		if (item != NULL) {
291 			float stringWidth = menu->StringWidth(item->Label());
292 			maxWidth = max_c(maxWidth, stringWidth);
293 		}
294 	}
295 
296 	maxWidth += _mField->StringWidth(BControl::Label()) + kLabelSpace + kWidthModifier;
297 	if (width != NULL)
298 		*width = maxWidth;
299 }
300 
301 
302 /*! \brief Resizes the control to its preferred size.
303 */
304 void
305 BOptionPopUp::ResizeToPreferred()
306 {
307 	// TODO: Some more work is needed either here or in GetPreferredSize(),
308 	// since the control doesnt' always resize as it should.
309 	float width, height;
310 	GetPreferredSize(&width, &height);
311 	ResizeTo(width, height);
312 
313 	float newWidth = _mField->StringWidth(BControl::Label());
314 	_mField->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 = _mField->Menu();
327 	if (menu != NULL) {
328 		BMenuItem *marked = menu->FindMarked();
329 		if (marked != NULL) {
330 			if (outName != NULL)
331 				*outName = marked->Label();
332 			if (outValue != NULL)
333 				*outValue = marked->Message()->FindInt32("be:value", outValue);
334 
335 			return menu->IndexOf(marked);
336 		}
337 	}
338 
339 	return B_ERROR;
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