1: <?php
2:
3: /**
4: * This file is part of the Nette Framework (http://nette.org)
5: *
6: * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7: *
8: * For the full copyright and license information, please view
9: * the file license.txt that was distributed with this source code.
10: */
11:
12: namespace Nette\Forms\Controls;
13:
14: use Nette;
15:
16:
17:
18: /**
19: * Select box control that allows single item selection.
20: *
21: * @author David Grudl
22: *
23: * @property-read mixed $rawValue
24: * @property bool $prompt
25: * @property array $items
26: * @property-read string $selectedItem
27: */
28: class SelectBox extends BaseControl
29: {
30: /** @var array */
31: private $items = array();
32:
33: /** @var array */
34: protected $allowed = array();
35:
36: /** @var mixed */
37: private $prompt = FALSE;
38:
39: /** @var bool */
40: private $useKeys = TRUE;
41:
42:
43:
44: /**
45: * @param string label
46: * @param array items from which to choose
47: * @param int number of rows that should be visible
48: */
49: public function __construct($label = NULL, array $items = NULL, $size = NULL)
50: {
51: parent::__construct($label);
52: $this->control->setName('select');
53: $this->control->size = $size > 1 ? (int) $size : NULL;
54: if ($items !== NULL) {
55: $this->setItems($items);
56: }
57: }
58:
59:
60:
61: /**
62: * Returns selected item key.
63: * @return mixed
64: */
65: public function getValue()
66: {
67: $allowed = $this->allowed;
68: if ($this->prompt) {
69: $allowed = array_slice($allowed, 1, count($allowed), TRUE);
70: }
71:
72: return is_scalar($this->value) && isset($allowed[$this->value]) ? $this->value : NULL;
73: }
74:
75:
76:
77: /**
78: * Returns selected item key (not checked).
79: * @return mixed
80: */
81: public function getRawValue()
82: {
83: return is_scalar($this->value) ? $this->value : NULL;
84: }
85:
86:
87:
88: /**
89: * Has been any item selected?
90: * @return bool
91: */
92: public function isFilled()
93: {
94: $value = $this->getValue();
95: return is_array($value) ? count($value) > 0 : $value !== NULL;
96: }
97:
98:
99:
100: /**
101: * Ignores the first item in select box.
102: * @param string
103: * @return SelectBox provides a fluent interface
104: */
105: public function setPrompt($prompt)
106: {
107: $this->prompt = $prompt;
108: if ($prompt !== NULL && !is_bool($prompt)) {
109: $this->items = array('' => $prompt) + $this->items;
110: $this->allowed = array('' => '') + $this->allowed;
111: }
112: return $this;
113: }
114:
115:
116:
117: /** @deprecated */
118: function skipFirst($v = NULL)
119: {
120: trigger_error(__METHOD__ . '() is deprecated; use setPrompt() instead.', E_USER_WARNING);
121: return $this->setPrompt($v);
122: }
123:
124:
125:
126: /**
127: * Is first item in select box ignored?
128: * @return bool
129: */
130: final public function getPrompt()
131: {
132: return $this->prompt;
133: }
134:
135:
136:
137: /**
138: * Are the keys used?
139: * @return bool
140: */
141: final public function areKeysUsed()
142: {
143: return $this->useKeys;
144: }
145:
146:
147:
148: /**
149: * Sets items from which to choose.
150: * @param array
151: * @param bool
152: * @return SelectBox provides a fluent interface
153: */
154: public function setItems(array $items, $useKeys = TRUE)
155: {
156: $this->items = $items;
157: if ($this->prompt) {
158: $this->items = array('' => is_bool($this->prompt) ? $this->items[''] : $this->prompt) + $this->items;
159: }
160: $this->allowed = $this->prompt ? array('' => '') : array();
161: $this->useKeys = (bool) $useKeys;
162:
163: foreach ($items as $key => $value) {
164: if (!is_array($value)) {
165: $value = array($key => $value);
166: }
167:
168: foreach ($value as $key2 => $value2) {
169: if (!$this->useKeys) {
170: if (!is_scalar($value2)) {
171: throw new Nette\InvalidArgumentException("All items must be scalar.");
172: }
173: $key2 = $value2;
174: }
175:
176: if (isset($this->allowed[$key2])) {
177: throw new Nette\InvalidArgumentException("Items contain duplication for key '$key2'.");
178: }
179:
180: $this->allowed[$key2] = $value2;
181: }
182: }
183: return $this;
184: }
185:
186:
187:
188: /**
189: * Returns items from which to choose.
190: * @return array
191: */
192: final public function getItems()
193: {
194: return $this->items;
195: }
196:
197:
198:
199: /**
200: * Returns selected value.
201: * @return string
202: */
203: public function getSelectedItem()
204: {
205: if (!$this->useKeys) {
206: return $this->getValue();
207:
208: } else {
209: $value = $this->getValue();
210: return $value === NULL ? NULL : $this->allowed[$value];
211: }
212: }
213:
214:
215:
216: /**
217: * Generates control's HTML element.
218: * @return Nette\Utils\Html
219: */
220: public function getControl()
221: {
222: $control = parent::getControl();
223: if ($this->prompt) {
224: reset($this->items);
225: $control->data('nette-empty-value', $this->useKeys ? key($this->items) : current($this->items));
226: }
227: $selected = $this->getValue();
228: $selected = is_array($selected) ? array_flip($selected) : array($selected => TRUE);
229: $option = Nette\Utils\Html::el('option');
230:
231: foreach ($this->items as $key => $value) {
232: if (!is_array($value)) {
233: $value = array($key => $value);
234: $dest = $control;
235:
236: } else {
237: $dest = $control->create('optgroup')->label($this->translate($key));
238: }
239:
240: foreach ($value as $key2 => $value2) {
241: if ($value2 instanceof Nette\Utils\Html) {
242: $dest->add((string) $value2->selected(isset($selected[$key2])));
243:
244: } else {
245: $key2 = $this->useKeys ? $key2 : $value2;
246: $value2 = $this->translate((string) $value2);
247: $dest->add((string) $option->value($key2)
248: ->selected(isset($selected[$key2]))
249: ->setText($value2));
250: }
251: }
252: }
253: return $control;
254: }
255:
256: }
257: